Appréhender Sphero
Dans la foulée du billet de Jonathan sur la manière de lister les objets Bluetooth connectés à notre Windows Phone, voici une petite initiation au développement d’application connectée à Sphero.
Premièrement, voici un petit rappel sur l’objet lui-même.
La Sphero est une petit boule de plastique téléguidée réalisée par la société Orbotix. Elle renferme un moteur et un accéléromètre qui lui permettent de se déplacer dans toutes les directions. Elle contient également une LED dont on peut faire varier la couleur. Voilà pour les quelques commandes basiques que nous aborderons dans ce sujet.
Les développeurs de Sphero fournissent des SDK pour presque toutes les plateformes mobiles mais pas encore pour Windows Phone. Voilà donc l’enjeu : vous montrer comment dompter la bête depuis votre téléphone.
Prérequis
Les prérequis sont identiques à ceux de l’article de Jonathan sur le Bluetooth.
La Sphero doit bien évidemment avoir été pairée au téléphone et le manifeste mis à jour.
Un peu de code
Préparation
Dans un nouveau projet Windows Phone, ajoutez ces 3 boutons à votre page principale :
<StackPanel VerticalAlignment="Center"> <Button x:Name="BtnConnect" Content="Connect To sphero" Click="Bluetooth_ConnectToSphero" /> <Button x:Name="BtnRandomColor" Content="Random Color" IsEnabled="False" Click="Bluetooth_SpheroToRandomColour" /> <Button x:Name="BtnRoll" Content="Roll" IsEnabled="False" Click="Bluetooth_RandomRoll" /> </StackPanel>
Puis, dans le code-behind de cette même page :
private Sphero _sphero; private async void Bluetooth_ConnectToSphero(object sender, RoutedEventArgs e) { try { _sphero = new Sphero(); await _sphero.ConnectAsync(); BtnConnect.IsEnabled = false; BtnRandomColor.IsEnabled = true; BtnRoll.IsEnabled = true; } catch (SpheroNotFoundException ex) { MessageBox.Show(ex.Message); } catch (SpheroConnectException ex) { MessageBox.Show(ex.Message); } } private async void Bluetooth_SpheroToRandomColour(object sender, RoutedEventArgs e) { try { await _sphero.RandomizeColourAsync(); } catch (SpheroNotConnectedException ex) { MessageBox.Show(ex.Message); } } private async void Bluetooth_RandomRoll(object sender, RoutedEventArgs e) { try { Random random = new Random(); await _sphero.RollAsync(random.Next(0, 359), 255); } catch (SpheroNotConnectedException ex) { MessageBox.Show(ex.Message); } }
Comme vous l’avez compris, nous allons nous connecter à la Sphero, changer sa couleur et la faire rouler.
Connexion
Nous allons maintenant passer aux choses sérieuses. Il vous faut tout d’abord créer une classe Sphero qui contiendra la logique de connexion et de commandes.
public class Sphero { #region "Properties" private StreamSocket _spheroSocket; #endregion #region "Public Methods" public async Task ConnectAsync() { try { PeerFinder.AlternateIdentities["Bluetooth:Paired"] = ""; var peers = await PeerFinder.FindAllPeersAsync(); if (!peers.Any(p => p.DisplayName.Contains("Sphero"))) { throw new SpheroNotFoundException(); } else { PeerInformation spheroPeer = peers.First(p => p.DisplayName.Contains("Sphero")); _spheroSocket = new StreamSocket(); await _spheroSocket.ConnectAsync(spheroPeer.HostName, "1"); } } catch (Exception) { throw new SpheroConnectException(); } } #endregion #region "Private Methods" private IAsyncOperationWithProgress<uint, uint> SendCommandAsync(byte[] package) { return _spheroSocket.OutputStream.WriteAsync(GetBufferFromByteArray(package)); } private static IBuffer GetBufferFromByteArray(byte[] package) { using (var dw = new DataWriter()) { dw.WriteBytes(package); return dw.DetachBuffer(); } } #endregion }
La méthode ConnectAsync liste les appareils pairés au téléphone, filtre ceux contenant le mot Sphero et tente de se connecter au premier en ouvrant une socket de connexion.
De la couleur
La méthode RandomizeColour est notre première commande. Elle instancie une couleur aléatoire en RGB et envoie au Sphero la commande lui ordonnant de prendre cette couleur.
Les commandes ont un format précis qu’il faut respecter avant d’envoyer les paquets :
SOP1 | SOP2 | DID | CID | SEQ | DLEN | <Data> | CHK |
Start of Packet 1 | Start of Packet 2 | Device ID | Command ID | Sequence Number | Data Length | Data | Checksum |
Nous n’aborderons pas dans ce sujet le détail des commandes mais il est important de comprendre que chaque commande est constituée d’un paquet de données formatées à construire et à transmettre.
Dans la classe Sphero, ajoutez :
public async Task RandomizeColourAsync() { if (_spheroSocket == null) { throw new SpheroNotConnectedException(); } Random random = new Random(); Color randomColor = Color.FromArgb(254, (byte) random.Next(1, 254), (byte) random.Next(1, 254), (byte) random.Next(1, 254)); byte[] package = new ColorLedCommand(randomColor).ToPacket(); await SendCommandAsync(package); }
et
#region "Private Methods" private IAsyncOperationWithProgress<uint, uint> SendCommandAsync(byte[] package) { return _spheroSocket.OutputStream.WriteAsync(GetBufferFromByteArray(package)); } private static IBuffer GetBufferFromByteArray(byte[] package) { using (var dw = new DataWriter()) { dw.WriteBytes(package); return dw.DetachBuffer(); } } #endregion
Ne nous reste plus qu’à créer la-dite commande :
public class ColorLedCommand { Color _color; public ColorLedCommand(Color color) { _color = color; } public byte[] ToPacket() { int checksum = 255 - ((2 + 32 + 1 + 5 + _color.R + _color.G + _color.B + 0) % 256); var byteList = new List<byte>(); byteList.Add((byte)(255)); // SOP1 byteList.Add((byte)(255)); // SOP2 byteList.Add((byte)(2)); // DID byteList.Add((byte)(32)); // CID byteList.Add((byte)(1)); // SEQ byteList.Add((byte)(5)); // DLEN byteList.Add((byte)(_color.R)); // RED byteList.Add((byte)(_color.G)); // GREEN byteList.Add((byte)(_color.B)); // BLUE byteList.Add((byte)(0)); // FLAG byteList.Add((byte)(checksum)); // CHK return byteList.ToArray(); } }
Elle bouge !
Toujours pas rassasié ? Voici la méthode à ajouter à notre classe Sphero et la commande associée pour la faire rouler :
public async Task RollAsync(int direction, int speed) { byte[] package = new RollCommand(direction, speed).ToPacket(); await SendCommandAsync(package); }
class RollCommand { readonly int _orientation; readonly int _speed; public RollCommand(int heading, int speed) { _orientation = heading; _speed = speed; } public byte[] ToPacket() { var lowByte = (byte)(_orientation & 0xff); var highByte = (byte)((_orientation >> 8) & 0xff); int checksum = 255 - ((2 + 48 + 1 + 5 + _speed + highByte + lowByte + 1) % 256); var byteList = new List<byte>(); byteList.Add((byte)(255)); // SOP1 byteList.Add((byte)(255)); // SOP2 byteList.Add((byte)(2)); // DID byteList.Add((byte)(48)); // CID byteList.Add((byte)(1)); // SEQ byteList.Add((byte)(5)); // DLEN byteList.Add((byte)(_speed)); // SPEED byteList.Add((byte)(highByte)); // HEADING(16bit)1 byteList.Add((byte)(lowByte)); // HEADING(16bit)2 byteList.Add((byte)(1)); // STATE byteList.Add((byte)(checksum)); // CHK return byteList.ToArray(); } }
Je n’ai pas décrit les exceptions spécifiques que j’utilise mais vous saurez comment les créer. je vous invite également à parcourir la documentation Sphero à l’adresse developer.gosphero.com . Vous y retrouverez également quelques commandes supplémentaires permettant de changer l’orientation de Sphero ainsi que la luminosité de la BackLed etc. Bref, de quoi vous amuser.
Commentaires