Skip to content

Commit 14e1688

Browse files
authored
Add DuploTrainBaseHub (#125)
* Add DuploTrainBaseHub - incl. Speaker, Motor, Voltage, RgbLight - incl. Speedometer, ColorSensor - Disable Scaling Support (used for Speedometer Count) #124 non-breaking
1 parent 3c1e0cf commit 14e1688

17 files changed

Lines changed: 588 additions & 10 deletions

README.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,8 @@ DI Container Elements
248248
- [X] Two Port Hub (88009)
249249
- [X] Two Port Handset (88010)
250250
- [X] Technic Medium Hub (88012)
251-
- [X] MarioHub (71360)
251+
- [X] MarioHub (set 71360)
252+
- [X] Duplo Train Base (set 10874)
252253
- .. other hubs depend on availability of hardware / contributions
253254
- Devices
254255
- [X] Technic Medium Hub (88012) - Rgb Light
@@ -262,10 +263,15 @@ DI Container Elements
262263
- [X] Hub (88009) - Rgb Light
263264
- [X] Hub (88009) - Current
264265
- [X] Hub (88009) - Voltage
265-
- [X] Mario Hub (71360) - Accelerometer (Raw & Gesture) (⚠ Usable but Gesture mapping is a rough draft)
266-
- [X] Mario Hub (71360) - TagSensor (Barcode & RGB)
267-
- [X] Mario Hub (71360) - Pants
268-
- [ ] Mario Hub (71360) - Debug
266+
- [X] Mario Hub (set 71360) - Accelerometer (Raw & Gesture) (⚠ Usable but Gesture mapping is a rough draft)
267+
- [X] Mario Hub (set 71360) - TagSensor (Barcode & RGB)
268+
- [X] Mario Hub (set 71360) - Pants
269+
- [ ] Mario Hub (set 71360) - Debug
270+
- [X] Duplo Train Base (set 10874) - Motor
271+
- [X] Duplo Train Base (set 10874) - Speaker
272+
- [X] Duplo Train Base (set 10874) - Rgb Light
273+
- [X] Duplo Train Base (set 10874) - ColorSensor
274+
- [X] Duplo Train Base (set 10874) - Speedometer
269275
- [X] Medium Linear Motor (88008)
270276
- [X] Remote Control Button (88010)
271277
- [X] Remote Control RSSI (88010)
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
using Microsoft.Extensions.Logging;
4+
using SharpBrick.PoweredUp;
5+
6+
namespace Example
7+
{
8+
public class ExampleDuploTrainBase : BaseExample
9+
{
10+
public override async Task ExecuteAsync()
11+
{
12+
using (var train = Host.FindByType<DuploTrainBaseHub>())
13+
{
14+
await train.VerifyDeploymentModelAsync(modelBuilder => modelBuilder
15+
.AddHub<DuploTrainBaseHub>(hubBuilder => { })
16+
);
17+
18+
using var d1 = train.Voltage.VoltageSObservable.Subscribe(x => Log.LogWarning($"Voltage: {x.Pct}% / {x.SI}"));
19+
using var d2 = train.Motor.OnSecondsObservable.Subscribe(x => Log.LogWarning($"Seconds: {x}"));
20+
using var d3 = train.Speedometer.SpeedObservable.Subscribe(x => Log.LogWarning($"Speed: {x.Pct}% / {x.SI}"));
21+
using var d4 = train.Speedometer.CountObservable.Subscribe(x => Log.LogWarning($"Count: {x}"));
22+
using var d5 = train.ColorSensor.ColorObservable.Subscribe(x => Log.LogWarning($"Color: {x}"));
23+
using var d6 = train.ColorSensor.ColorTagObservable.Subscribe(x => Log.LogWarning($"Color Tag: {x}")); // does not work
24+
using var d7 = train.ColorSensor.ReflectionObservable.Subscribe(x => Log.LogWarning($"Reflection: {x}"));
25+
using var d8 = train.ColorSensor.RgbObservable.Subscribe(x => Log.LogWarning($"RGB {x.red}/{x.green}/{x.blue}"));
26+
27+
await train.Voltage.SetupNotificationAsync(train.Voltage.ModeIndexVoltageS, true);
28+
29+
// Motor: motor can either be queried for seconds active OR be instructed to run
30+
//await train.Motor.SetupNotificationAsync(train.Motor.ModeIndexOnSec, false);
31+
32+
await train.Speedometer.TryLockDeviceForCombinedModeNotificationSetupAsync(train.Speedometer.ModeIndexSpeed, train.Speedometer.ModeIndexCount);
33+
await train.Speedometer.SetupNotificationAsync(train.Speedometer.ModeIndexSpeed, true, deltaInterval: 1);
34+
await train.Speedometer.SetupNotificationAsync(train.Speedometer.ModeIndexCount, true, deltaInterval: 1);
35+
await train.Speedometer.UnlockFromCombinedModeNotificationSetupAsync(true);
36+
37+
// ColorSensor: either this combination
38+
// await train.ColorSensor.TryLockDeviceForCombinedModeNotificationSetupAsync(train.ColorSensor.ModeIndexColor, train.ColorSensor.ModeIndexReflection, train.ColorSensor.ModeIndexRgb);
39+
// await train.ColorSensor.SetupNotificationAsync(train.ColorSensor.ModeIndexColor, true, deltaInterval: 1);
40+
// await train.ColorSensor.SetupNotificationAsync(train.ColorSensor.ModeIndexReflection, true, deltaInterval: 1);
41+
// await train.ColorSensor.SetupNotificationAsync(train.ColorSensor.ModeIndexRgb, true, deltaInterval: 1);
42+
// await train.ColorSensor.UnlockFromCombinedModeNotificationSetupAsync(true);
43+
44+
// ColorSensor: or standalone
45+
await train.ColorSensor.SetupNotificationAsync(train.ColorSensor.ModeIndexColorTag, true, deltaInterval: 1);
46+
47+
await train.Speaker.PlaySoundAsync(DuploTrainBaseSound.Horn);
48+
await train.RgbLight.SetRgbColorNoAsync(PoweredUpColor.Red);
49+
await Task.Delay(1_000);
50+
await train.RgbLight.SetRgbColorNoAsync(PoweredUpColor.Yellow);
51+
await Task.Delay(1_000);
52+
await train.RgbLight.SetRgbColorNoAsync(PoweredUpColor.Green);
53+
await train.Speaker.PlaySoundAsync(DuploTrainBaseSound.StationDeparture);
54+
55+
await train.Motor.StartPowerAsync(40);
56+
await Task.Delay(3_000);
57+
await train.Speaker.PlaySoundAsync(DuploTrainBaseSound.Steam);
58+
await train.Motor.StartPowerAsync(-40);
59+
await Task.Delay(3_000);
60+
await train.Speaker.PlaySoundAsync(DuploTrainBaseSound.Brake);
61+
await train.Motor.StopByFloatAsync();
62+
await Task.Delay(1_000);
63+
await train.Speaker.PlaySoundAsync(DuploTrainBaseSound.WaterRefill);
64+
65+
await Task.Delay(1_000);
66+
67+
await train.SwitchOffAsync();
68+
}
69+
}
70+
}
71+
}

examples/SharpBrick.PoweredUp.Examples/Program.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ static async Task Main(string[] args)
3939
//example = new Example.ExampleTechnicMediumAngularMotorGrey();
4040
//example = new Example.ExampleMarioBarcode();
4141
//example = new Example.ExampleMarioPants();
42-
example = new Example.ExampleMarioAccelerometer();
42+
//example = new Example.ExampleMarioAccelerometer();
43+
example = new Example.ExampleDuploTrainBase();
4344

4445
// NOTE: Examples are programmed object oriented style. Base class implements methods Configure, DiscoverAsync and ExecuteAsync to be overwriten on demand.
4546
await example.InitHostAndDiscoverAsync(enableTrace);

src/SharpBrick.PoweredUp/Devices/DeviceFactory.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ public Type GetTypeFromDeviceType(DeviceType deviceType)
5050
DeviceType.MarioHubPants => typeof(MarioHubPants),
5151
DeviceType.MarioHubDebug => typeof(MarioHubDebug),
5252
DeviceType.MediumLinearMotor => typeof(MediumLinearMotor),
53+
DeviceType.DuploTrainBaseMotor => typeof(DuploTrainBaseMotor),
54+
DeviceType.DuploTrainBaseSpeaker => typeof(DuploTrainBaseSpeaker),
55+
DeviceType.DuploTrainBaseColorSensor => typeof(DuploTrainBaseColorSensor),
56+
DeviceType.DuploTrainBaseSpeedometer => typeof(DuploTrainBaseSpeedometer),
5357
_ => null,
5458
};
5559

@@ -76,6 +80,10 @@ public static DeviceType GetDeviceTypeFromType(Type type)
7680
nameof(MarioHubPants) => DeviceType.MarioHubPants,
7781
nameof(MarioHubDebug) => DeviceType.MarioHubDebug,
7882
nameof(MediumLinearMotor) => DeviceType.MediumLinearMotor,
83+
nameof(DuploTrainBaseMotor) => DeviceType.DuploTrainBaseMotor,
84+
nameof(DuploTrainBaseSpeaker) => DeviceType.DuploTrainBaseSpeaker,
85+
nameof(DuploTrainBaseColorSensor) => DeviceType.DuploTrainBaseColorSensor,
86+
nameof(DuploTrainBaseSpeedometer) => DeviceType.DuploTrainBaseSpeedometer,
7987
_ => DeviceType.Unknown,
8088
};
8189
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reactive.Linq;
5+
using SharpBrick.PoweredUp.Protocol;
6+
using SharpBrick.PoweredUp.Utils;
7+
8+
namespace SharpBrick.PoweredUp
9+
{
10+
public class DuploTrainBaseColorSensor : Device, IPoweredUpDevice
11+
{
12+
protected SingleValueMode<sbyte> _colorMode;
13+
protected SingleValueMode<sbyte> _colorTagMode;
14+
protected SingleValueMode<sbyte> _reflectionMode;
15+
protected MultiValueMode<short> _rgbMode;
16+
17+
public byte ModeIndexColor { get; protected set; } = 0;
18+
public byte ModeIndexColorTag { get; protected set; } = 1;
19+
public byte ModeIndexReflection { get; protected set; } = 2;
20+
public byte ModeIndexRgb { get; protected set; } = 3;
21+
22+
public sbyte Color => _colorMode.SI;
23+
public DuploColorTag ColorTag => (DuploColorTag)_colorTagMode.SI;
24+
public sbyte Reflection => _reflectionMode.SI;
25+
public (short red, short green, short blue) Rgb => (_rgbMode.SI[0], _rgbMode.SI[1], _rgbMode.SI[2]);
26+
public (short red, short green, short blue) RgbPct => (_rgbMode.Pct[0], _rgbMode.Pct[1], _rgbMode.Pct[2]);
27+
28+
public IObservable<sbyte> ColorObservable => _colorMode.Observable.Select(v => v.SI);
29+
public IObservable<DuploColorTag> ColorTagObservable => _colorTagMode.Observable.Select(v => (DuploColorTag)v.SI);
30+
public IObservable<sbyte> ReflectionObservable => _reflectionMode.Observable.Select(v => v.SI);
31+
public IObservable<(short red, short green, short blue)> RgbObservable => _rgbMode.Observable.Select(v => (v.SI[0], v.SI[1], v.SI[2]));
32+
public IObservable<(short red, short green, short blue)> RgbPctObservable => _rgbMode.Observable.Select(v => (v.Pct[0], v.Pct[1], v.Pct[2]));
33+
34+
public DuploTrainBaseColorSensor()
35+
{ }
36+
37+
public DuploTrainBaseColorSensor(ILegoWirelessProtocol protocol, byte hubId, byte portId)
38+
: base(protocol, hubId, portId)
39+
{
40+
_colorMode = SingleValueMode<sbyte>(ModeIndexColor);
41+
_colorTagMode = SingleValueMode<sbyte>(ModeIndexColorTag);
42+
_reflectionMode = SingleValueMode<sbyte>(ModeIndexReflection);
43+
_rgbMode = MultiValueMode<short>(ModeIndexRgb);
44+
45+
ObserveForPropertyChanged(_colorMode.Observable, nameof(Color));
46+
ObserveForPropertyChanged(_colorTagMode.Observable, nameof(ColorTag));
47+
ObserveForPropertyChanged(_reflectionMode.Observable, nameof(Reflection));
48+
ObserveForPropertyChanged(_rgbMode.Observable, nameof(Rgb), nameof(RgbPct));
49+
}
50+
51+
public IEnumerable<byte[]> GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType)
52+
=> @"
53+
0B-00-43-12-01-06-05-1F-00-00-00
54+
07-00-43-12-02-0F-00
55+
11-00-44-12-00-00-43-4F-4C-4F-52-00-00-00-00-00-00
56+
0E-00-44-12-00-01-00-00-00-00-00-00-20-41
57+
0E-00-44-12-00-02-00-00-00-00-00-00-C8-42
58+
0E-00-44-12-00-03-00-00-00-00-00-00-20-41
59+
0A-00-44-12-00-04-69-64-78-00
60+
08-00-44-12-00-05-84-00
61+
0A-00-44-12-00-80-01-00-03-00
62+
11-00-44-12-01-00-43-20-54-41-47-00-00-00-00-00-00
63+
0E-00-44-12-01-01-00-00-00-00-00-00-20-41
64+
0E-00-44-12-01-02-00-00-00-00-00-00-C8-42
65+
0E-00-44-12-01-03-00-00-00-00-00-00-20-41
66+
0A-00-44-12-01-04-69-64-78-00
67+
08-00-44-12-01-05-C4-00
68+
0A-00-44-12-01-80-01-00-03-00
69+
11-00-44-12-02-00-52-45-46-4C-54-00-00-00-00-00-00
70+
0E-00-44-12-02-01-00-00-00-00-00-00-C8-42
71+
0E-00-44-12-02-02-00-00-00-00-00-00-C8-42
72+
0E-00-44-12-02-03-00-00-00-00-00-00-C8-42
73+
0A-00-44-12-02-04-72-61-77-00
74+
08-00-44-12-02-05-10-00
75+
0A-00-44-12-02-80-01-00-03-00
76+
11-00-44-12-03-00-52-47-42-20-49-00-00-00-00-00-00
77+
0E-00-44-12-03-01-00-00-00-00-00-C0-7F-44
78+
0E-00-44-12-03-02-00-00-00-00-00-00-C8-42
79+
0E-00-44-12-03-03-00-00-00-00-00-C0-7F-44
80+
0A-00-44-12-03-04-72-61-77-00
81+
08-00-44-12-03-05-10-00
82+
0A-00-44-12-03-80-03-01-05-00
83+
11-00-44-12-04-00-43-41-4C-49-42-00-00-00-00-00-00
84+
0E-00-44-12-04-01-00-00-00-00-00-00-FA-45
85+
0E-00-44-12-04-02-00-00-00-00-00-00-C8-42
86+
0E-00-44-12-04-03-00-00-00-00-00-00-FA-45
87+
0A-00-44-12-04-04-00-00-00-00
88+
08-00-44-12-04-05-10-00
89+
0A-00-44-12-04-80-03-01-05-00
90+
".Trim().Split("\n").Select(s => BytesStringUtil.StringToData(s));
91+
}
92+
}
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reactive.Linq;
5+
using System.Threading.Tasks;
6+
using SharpBrick.PoweredUp.Protocol;
7+
using SharpBrick.PoweredUp.Protocol.Knowledge;
8+
using SharpBrick.PoweredUp.Protocol.Messages;
9+
using SharpBrick.PoweredUp.Utils;
10+
11+
namespace SharpBrick.PoweredUp
12+
{
13+
public class DuploTrainBaseMotor : Device, IPoweredUpDevice
14+
{
15+
public SingleValueMode<int> _onSecMode;
16+
17+
public byte ModeIndexMotor { get; protected set; } = 0;
18+
public byte ModeIndexOnSec { get; protected set; } = 1;
19+
20+
public int OnSeconds => _onSecMode.SI;
21+
public IObservable<int> OnSecondsObservable => _onSecMode.Observable.Select(x => x.SI);
22+
23+
public DuploTrainBaseMotor()
24+
{ }
25+
26+
public DuploTrainBaseMotor(ILegoWirelessProtocol protocol, byte hubId, byte portId)
27+
: base(protocol, hubId, portId)
28+
{
29+
_onSecMode = SingleValueMode<int>(ModeIndexOnSec);
30+
31+
ObserveForPropertyChanged(_onSecMode.Observable, nameof(OnSeconds));
32+
}
33+
34+
public void ExtendPortMode(PortModeInfo modeInfo)
35+
{
36+
if (modeInfo.ModeIndex == ModeIndexOnSec)
37+
{
38+
modeInfo.DisablePercentage = true;
39+
}
40+
}
41+
42+
/// <summary>
43+
/// Starts the motor with full speed at the given power level.
44+
/// </summary>
45+
/// <param name="power">
46+
/// - Power levels in percentage: 1 - 100 (CW), -1 - -100 (CCW)
47+
/// - Stop Motor (floating): 0
48+
/// - Stop Motor (breaking): 127
49+
/// </param>
50+
/// <returns>An awaitable Task.</returns>
51+
public async Task<PortFeedback> StartPowerAsync(sbyte power)
52+
{
53+
AssertValidPower(power, nameof(power));
54+
AssertIsConnected();
55+
56+
var response = await _protocol.SendPortOutputCommandAsync(new PortOutputCommandStartPowerMessage()
57+
{
58+
HubId = _hubId,
59+
PortId = _portId,
60+
StartupInformation = PortOutputCommandStartupInformation.ExecuteImmediately,
61+
CompletionInformation = PortOutputCommandCompletionInformation.CommandFeedback,
62+
Power = power,
63+
});
64+
65+
return response;
66+
}
67+
68+
/// <summary>
69+
/// Stops the motor (brake; no movement afterwards)
70+
/// </summary>
71+
/// <returns></returns>
72+
public Task StopByBrakeAsync()
73+
=> StartPowerAsync((sbyte)SpecialSpeed.Brake);
74+
75+
/// <summary>
76+
/// Stops the motor (float; freely floating by inertia)
77+
/// </summary>
78+
/// <returns></returns>
79+
public Task StopByFloatAsync()
80+
=> StartPowerAsync((sbyte)SpecialSpeed.Float);
81+
82+
/// <summary>
83+
/// Start the pair on motors with full speed on given power values.
84+
/// </summary>
85+
/// <param name="powerOnMotor1">
86+
/// - Power levels in percentage: 1 - 100 (CW), -1 - -100 (CCW)
87+
/// - Stop Motor (floating): 0
88+
/// - Stop Motor (breaking): 127
89+
/// </param>
90+
/// <param name="powerOnMotor2">
91+
/// - Power levels in percentage: 1 - 100 (CW), -1 - -100 (CCW)
92+
/// - Stop Motor (floating): 0
93+
/// - Stop Motor (breaking): 127
94+
/// </param>
95+
/// <returns></returns>
96+
public async Task<PortFeedback> StartPowerAsync(sbyte powerOnMotor1, sbyte powerOnMotor2)
97+
{
98+
AssertValidPower(powerOnMotor1, nameof(powerOnMotor1));
99+
AssertValidPower(powerOnMotor2, nameof(powerOnMotor2));
100+
AssertIsConnected();
101+
AssertIsVirtualPort();
102+
103+
var response = await _protocol.SendPortOutputCommandAsync(new PortOutputCommandStartPower2Message()
104+
{
105+
HubId = _hubId,
106+
PortId = _portId,
107+
StartupInformation = PortOutputCommandStartupInformation.ExecuteImmediately,
108+
CompletionInformation = PortOutputCommandCompletionInformation.CommandFeedback,
109+
Power1 = powerOnMotor1,
110+
Power2 = powerOnMotor2,
111+
});
112+
113+
return response;
114+
}
115+
116+
protected void AssertValidPower(sbyte power, string argumentName)
117+
{
118+
if (
119+
power < -100 ||
120+
(power > 100 && power != 127)
121+
)
122+
{
123+
throw new ArgumentOutOfRangeException(argumentName);
124+
}
125+
}
126+
127+
public IEnumerable<byte[]> GetStaticPortInfoMessages(Version softwareVersion, Version hardwareVersion, SystemType systemType)
128+
=> @"
129+
0B-00-43-00-01-03-02-02-00-01-00
130+
05-00-43-00-02
131+
11-00-44-00-00-00-54-20-4D-4F-54-00-00-00-00-00-00
132+
0E-00-44-00-00-01-00-00-C8-C2-00-00-C8-42
133+
0E-00-44-00-00-02-00-00-C8-C2-00-00-C8-42
134+
0E-00-44-00-00-03-00-00-C8-C2-00-00-C8-42
135+
0A-00-44-00-00-04-70-77-72-00
136+
08-00-44-00-00-05-00-50
137+
0A-00-44-00-00-80-01-00-03-00
138+
11-00-44-00-01-00-4F-4E-53-45-43-00-00-00-00-00-00
139+
0E-00-44-00-01-01-00-00-00-00-00-00-80-3F
140+
0E-00-44-00-01-02-00-00-00-00-00-00-C8-42
141+
0E-00-44-00-01-03-00-00-00-00-00-00-80-3F
142+
0A-00-44-00-01-04-73-65-63-00
143+
08-00-44-00-01-05-08-00
144+
0A-00-44-00-01-80-01-02-04-00
145+
".Trim().Split("\n").Select(s => BytesStringUtil.StringToData(s));
146+
}
147+
}

0 commit comments

Comments
 (0)