📜 ⬆️ ⬇️

Developing a game running WP8 using Netduino

We faced an interesting article from an application developer for Windows Phone and decided to share it with you.

When I was little, my parents gave me a wooden maze game. I really liked her. I don’t think that the labyrinth itself ever bothered me, but the tilt mechanism was intriguing and very simple.

Later I remembered this game and decided to find this game online and my old maze in the closet.
')
Now there are a lot of similar games that use the accelerometer of the phone, but their graphics leaves much to be desired. That's why I made a photorealistic version ;-)






The goal was to make a maze that combines old-school and modern versions of the game. So I used the phone to control the tilt of the physical maze via Bluetooth with Netduino. The labyrinth is a simple model printed on a 3D printer and tilted by two servo drives. A bonus from the addition of the switch at the end was the feedback to the phone, giving know about the end of the game.

What we need:

• 2 pretty strong servos. I used Turnigy TGY-9018MG Metal Gear Servo from HobbyKing
• Bluetooth module
• Netduino
• Small square maze
• 10 kΩ resistor
• Ball of conductive material

Mechanism:

Before going into technical details, it is worthwhile to understand the tilting mechanism. The description below may be confusing, as it is difficult to explain. So if you feel that you are confused, look at the pictures below, it will become clearer.

The slope in two directions is implemented by analogy with a wooden game. One servo is attached to the inside of the toy, then the second servo is attached to the outer case. It should be noted that in the wooden maze, three boxes are placed one inside the other, as can be seen in the first picture of the post. I used the same theory, but somewhat simplified it. The lower part of both axes will not be truly horizontal, which means that the labyrinth will in fact rotate around a conditional internal rod.

So we just have two servo drives located at 90 degrees relative to each other. The first will rotate the second. And the second one, in turn, will rotate the stand attached to the maze itself. Below are two pictures that illustrate the above.



And to make it even clearer, here’s how it looks in action:



To connect the drives, I just pulled them tight with a rubber band. I glued the lower servo to the old mount from the drive and fixed it on a flat base.

Labyrinth:

With the model of the maze I had to suffer a little, but, anyway, the model at the end of the post is quite working, if you wish, you can print it on your own printer. I printed it at a size of 9 by 9 cm, because This is the largest size that my Makerbot Thing-O-Matic can print.



To create a model, I first went to www.mazegenerator.net and created a 9x9 labyrinth there. Then I imported the result into SketchUp, painted the lines and built up the walls. I made the walls so high that the ball did not fall out of the maze, but did not get stuck.

Netduino:

The electronic part is not so complicated.



Note: the common wires are arranged so as to be more visible.

Bluetooth module:

In this case, he joins the same way as in my previous projects. The code is exactly the same, so you can read the details here:
blog.roguecode.co.za/Post/ControllingaNetduinooverBluetoothwithWP8
blog.roguecode.co.za/Post/MoreNetduino%2bWP8%2bBluetoothfun-3Dreconstruction
blog.roguecode.co.za/Post/Netduino%2bSonar%2bWP8%2bBluetooth-Controllingsoundwithyourmind

Drives:

I started this section by directly connecting the servo drives to the Netduino power connector. I understand that in general it is not recommended to do this, because it leads to a large expenditure of energy, but for the launch of the concept this should be acceptable.

In addition, I had other problems besides the power consumption - as soon as the servo drives started, the Bluetooth module was disconnected. This is perfectly understandable by noise / interference, but I did not come across the fact that it affected any other component so clearly.

So I connected them to a 4.8V battery pack. It is important to remember that it is necessary to connect the battery ground and Netduino. Signal wires go to the PWM pins.

The end point of the maze:

To make the contacts of the end point of the labyrinth, I cut off two metal strips and placed one horizontally at the end point, and the second vertically, with a small gap relative to the first. When the ball reaches the end of the maze, it closes the contact between the two pads and activates the switch.



Code:

After adding the Bluetooth code (from the links above), the rest becomes quite simple.

static SerialPort bt; static string buffer = ""; static Servo servoX; static Servo servoY; static InterruptPort endStopPort; static bool isRunning = false; public static void Main() { servoX = new Servo(Pins.GPIO<em>PIN</em>D5); servoY = new Servo(Pins.GPIO<em>PIN</em>D9); bt = new SerialPort(SerialPorts.COM1, 9600, Parity.None, 8, StopBits.One); bt.Open(); endStopPort = new InterruptPort(Pins.GPIO<em>PIN</em>D10, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeLevelHigh); endStopPort.OnInterrupt += new NativeEventHandler(endStopPort<em>OnInterrupt); bt.DataReceived += new SerialDataReceivedEventHandler(bt</em>DataReceived); servoX.Degree = 90; servoY.Degree = 105; while (true) { //do some other stuff here Thread.Sleep(1000); } } static void endStopPort_OnInterrupt(uint data1, uint data2, DateTime time) { if (isRunning) { isRunning = false; byte[] bytes = Encoding.UTF8.GetBytes("done|"); bt.Write(bytes, 0, bytes.Length); } Thread.Sleep(1); endStopPort.ClearInterrupt(); } 


To begin with, we install both servo drives, Bluetooth, and a switch on the mating end of the maze. To understand how and why the switches work on Netduino, go to the link . Then we align the servos so that the maze is horizontal.

In an ideal world in which they are not held on elastic bands, both values ​​will be 90 degrees. The drive class is taken here .

The code in the event handler will cause the switch to fire when the ball closes the contacts. But, since we do not want this to work again and again, we will make sure that the current game is running (using the isRunning bool).

In the final part of the code, Netduino processes messages from the phone.

 private static void DoSomething(string buffer) { if (buffer == "start") { isRunning = true; } else { string[] split = buffer.Split(new char[] { ',' }); if (split.Length == 2) { int x = int.Parse(split[0]); int y = int.Parse(split[1]); servoX.Degree = x + 3; servoY.Degree = y + 12; Debug.Print(x + " " + y); } } } 


When a player presses the GO button on the phone, a “start” command is sent via Bluetooth. So we set the bool to true to indicate that the game has started.

If the message is not a start message, then we know that these are accelerometer values. As will be clear from the telephone part of the code, we send this data as values ​​along the X, Y axes. The code separates them, then converts them to int values ​​and sets the servos. As mentioned earlier, I add a slight offset, because my drives are not fully calibrated by level.

WP8:

The phone code is completely simple. Here are the basic functions that it performs:

- Send “start” when the GO button is pressed
- Display timer
- Send accelerometer values ​​along the X and Y axes
- Display the final game time when getting done

And here is the code for each of them:

Send “start” when the GO button is pressed and display the timer

 private void goBtn<em>Click</em>1(object sender, RoutedEventArgs e) { Write("start"); secTxt.Text = ""; msText.Text = ""; <em>startedDT = DateTime.Now; TimeSpan timeTaken; _timer = new DispatcherTimer { Interval = new TimeSpan(0, 0, 0, 0, 51) }; _timer.Tick += (s, ev) => { timeTaken = DateTime.Now.Subtract(</em>startedDT); secTxt.Text = timeTaken.Seconds.ToString(); msText.Text = timeTaken.Milliseconds.ToString(); }; _timer.Start(); goBtn.Visibility = System.Windows.Visibility.Collapsed; stopBtn.Visibility = System.Windows.Visibility.Visible; timerDisplayContainer.Visibility = System.Windows.Visibility.Visible; } 


It should be noted that using a DispatcherTimer with such a high frequency is probably not the best idea. And it should not be used except when it is really necessary.

Send accelerometer values ​​along the X and Y axes

  void <em>acc</em>ReadingChanged(Accelerometer sender, AccelerometerReadingChangedEventArgs args) { Write(Convert(args.Reading.AccelerationX) + "," + Convert(-args.Reading.AccelerationY) + "|"); Dispatcher.BeginInvoke(() => { xRight.Opacity = args.Reading.AccelerationX * 2; xLeft.Opacity = -args.Reading.AccelerationX * 2; yTop.Opacity = args.Reading.AccelerationY * 2; yBottom.Opacity = -args.Reading.AccelerationY * 2; }); } private string Convert(double val) { return ((int)((Clamp((val * 2d), -1, 1) * 10) + 90)).ToString(); //first double it so full range is -45deg to 45deg //clamp the above value to the max of -1 and 1 //then multiple by the max angle we want the servos to goto //then add 90 because servos go from 0 to 180, not -90 to 90 } private double Clamp(double value, double min, double max) { return value < min ? min : value > max ? max : value; } 


Those who used MathHelper.Clamp in XNA should recognize the functionality. It simply stops the value from exceeding or decreasing beyond the set limit. The code inside the Dispatcher does several visual actions to show the tilt angle in the UI.

Showing the final time after receiving “done”.

 private void DoSomethingWithReceivedString(string <em>receivedBuffer) { if (</em>receivedBuffer == "done") { <em>timer.Stop(); stopBtn.Visibility = System.Windows.Visibility.Collapsed; goBtn.Visibility = System.Windows.Visibility.Visible; TimeSpan timeTaken = DateTime.Now.Subtract(</em>startedDT); MessageBox.Show(string.Format("You took {0}:{1}", timeTaken.Seconds, timeTaken.Milliseconds), "Done!", MessageBoxButton.OK); } } 


That's all, in fact. See the source for examples of BT and UI.

Downloads:

The zip file contains a solution for Netduino, a solution for WP8 and an STL model for printing.

Share your projects that used WP8!

Source: https://habr.com/ru/post/174731/


All Articles