Мы столкнулись с интересной статьей от разработчика приложений под Windows Phone и решили поделиться ею с вами.
Когда я был маленьким, родители подарили мне деревянную игру-лабиринт. Она мне очень нравилась. Не думаю, что меня когда-либо волновал сам лабиринт, но механизм наклона был интригующим и очень простым.
Позже я вспомнил об этой игре и решил найти эту игру онлайн и свой старый лабиринт в кладовке.
Сейчас существует масса схожих игр, использующих акселерометр телефона, но их графика оставляет желать лучшего. Вот почему я сделал фотореалистичный вариант ;-)
Цель была сделать лабиринт, соединяющий в себе олдскульный и современный варианты игры. Так что я использовал телефон для управления наклоном физического лабиринта через Bluetooth с Netduino. Лабиринт — это простая модель, распечатанная на 3D принтере и наклоняемая двумя сервоприводами. Бонусом от добавления выключателя в конце послужила обратная связь с телефоном, дающая знать о завершения игры.
• 2 довольно сильных сервопривода. Я использовал Turnigy TGY-9018MG Metal Gear Servo из HobbyKing
• Модуль Bluetooth
• Netduino
• Небольшой квадратный лабиринт
• Резистор на 10 кОм
• Шарик из проводящего материала
Прежде чем вдаваться в технические подробности, стоит разобраться в наклоняющем механизме. Описание ниже может поставить в тупик, поскольку его сложно объяснить. Так что если почувствуете, что запутались, смотрите на картинки чуть ниже, станет понятнее.
Наклон в двух направлениях реализуют по аналогии с деревянной игрой. Один сервопривод прикрепляется к внутренней части игрушки, затем второй сервопривод крепится к внешнему корпусу. Стоит заметить, что в деревянном лабиринте целых три коробки помещены одна в другую, как это можно увидеть на первой картинке поста. Я пользовался той же теорией, но несколько ее упростил. Нижняя часть обеих осей не будет действительно горизонтальной, а значит, лабиринт по сути будет вращаться вокруг условного внутреннего стержня.
Так что мы просто имеем два сервопривода, расположенных под углом 90 градусов относительно друг друга. Первый будет вращать второй. А второй, в свою очередь, будет вращать подставку, прикрепленную к самому лабиринту. Ниже две картинки, иллюстрирующие вышеописанное.
И, чтобы стало еще понятнее, вот как это выглядит в действии:
Чтобы соединить приводы я просто туго стянул их резинкой. Нижний сервопривод я приклеил к старому креплению от привода и закрепил на плоском основании.
С моделью лабиринта пришлось немного помучиться, но, так или иначе, модель в конце поста вполне рабочая, её при желании можно распечатать на собственном принтере. Я распечатал его в размере 9 на 9 см, т.к. это самый большой размер, который может распечатать мой Makerbot Thing-O-Matic.
Чтобы создать модель, я первым делом отправился на www.mazegenerator.net и создал там лабиринт 9х9. Затем импортировал результат в SketchUp, отрисовал линии и нарастил стенки. Стенки я сделал высокими настолько, чтобы шарик не вываливался из лабиринта, но и не застревал.
Электронная часть не так уж сложна.
Примечание: общие провода расположены так, чтобы быть более заметными.
В данном случае он присоединяется также, как и в моих предыдущих проектах. Код точно такой же, так что подробности можно почитать здесь:
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
Я начал этот раздел с прямого подключения сервоприводов к разъёму питания Netduino. Понимаю, что вообще-то это не рекомендуется делать, поскольку это приводит к большим затратам энергии, но для запуска концепта это должно быть приемлемо.
Кроме того, у меня возникли другие проблемы, кроме потребляемой мощности – как только стартовали сервоприводы, происходило отключение модуля Bluetooth. Это вполне объяснимо шумом/помехами, но я не сталкивался с тем, чтобы это влияло на какой-либо другой компонент так явно.
Так что я подсоединил их к батарейному блоку на 4.8V. Важно помнить, что необходимо соединить заземление батареи и Netduino. Сигнальные провода идут к контактам ШИМ.
Чтобы сделать контакты конечной точки лабиринта, я отрезал две металлические полоски и поместил одну горизонтально в конечной точке, а вторую – вертикально, с небольшим зазором относительно первой. Когда шарик достигает конца лабиринта, он замыкает контакт между двумя площадками и активирует выключатель.
После добавления кода Bluetooth (по ссылкам выше) остальное становится довольно просто.
Для начала мы устанавливаем оба сервопривода, Bluetooth и выключатель на контактной площадке конца лабиринта. Чтобы разобраться, как и почему работают выключатели на Netduino, сходите по ссылке. Затем выравниваем сервоприводы, чтобы лабиринт был расположен горизонтально.
В идеальном мире, в котором они держатся не на резинках, оба значения будут 90 градусов. Класс приводов взят здесь.
Код в обработчике событий заставит выключатель срабатывать, когда шарик замкнет контакты. Но, поскольку мы не хотим, чтобы это срабатывало раз за разом, мы убедимся, что текущая игра запущена (с помощью isRunning bool).
В заключительной части кода Netduino обрабатывает сообщения от телефона.
Когда игрок нажимает кнопку GO на телефоне, по каналу Bluetooth посылается команда «start». Так что мы устанавливаем bool в значение true, чтобы показать, что игра началась.
Если сообщение – не сообщение о старте, то мы знаем, что это значения акселерометра. Как будет понятно из телефонной части кода, мы отправляем эти данные как значения по осям X, Y. Код разделяет их, затем конвертирует в значения int и устанавливает сервоприводы. Как упоминалось ранее, я добавляю небольшое смещение, поскольку мои приводы не до конца откалиброваны по уровню.
Телефонный код совершенно прост. Вот базовые функции, которые он выполняет:
— Послать «start», когда нажата кнопка GO
— Отобразить таймер
— Отправить значения акселерометра по осям X и Y
— Отобразить окончательное время игры при получении «done»
И вот код для каждой из них:
Послать «start», когда нажата кнопка GO и отобразить таймер
Следует отметить, что использовать DispatcherTimer с такой высокой частотой, вероятно, не самая лучшая идея. И он не должен использоваться кроме случаев, когда это действительно необходимо.
Отправить значения акселерометра по осям X и Y
Те, кто использовал MathHelper.Clamp в XNA должны узнать функционал. Он просто останавливает значение от превышения или снижения вне установленного лимита. Код внутри Dispatcher делает несколько визуальных действий, чтобы показать угол наклона в UI.
Показ финального времени после получения “done”.
Вот и все, по сути. Смотрите исходники для примеров BT и UI.
В zip-файле содержатся решение для Netduino, решение для WP8 и STL-модель для распечатывания.
Поделитесь вашими проектами, в которых использовался WP8!
Когда я был маленьким, родители подарили мне деревянную игру-лабиринт. Она мне очень нравилась. Не думаю, что меня когда-либо волновал сам лабиринт, но механизм наклона был интригующим и очень простым.
Позже я вспомнил об этой игре и решил найти эту игру онлайн и свой старый лабиринт в кладовке.
Сейчас существует масса схожих игр, использующих акселерометр телефона, но их графика оставляет желать лучшего. Вот почему я сделал фотореалистичный вариант ;-)
Цель была сделать лабиринт, соединяющий в себе олдскульный и современный варианты игры. Так что я использовал телефон для управления наклоном физического лабиринта через Bluetooth с Netduino. Лабиринт — это простая модель, распечатанная на 3D принтере и наклоняемая двумя сервоприводами. Бонусом от добавления выключателя в конце послужила обратная связь с телефоном, дающая знать о завершения игры.
Что нам понадобится:
• 2 довольно сильных сервопривода. Я использовал Turnigy TGY-9018MG Metal Gear Servo из HobbyKing
• Модуль Bluetooth
• Netduino
• Небольшой квадратный лабиринт
• Резистор на 10 кОм
• Шарик из проводящего материала
Механизм:
Прежде чем вдаваться в технические подробности, стоит разобраться в наклоняющем механизме. Описание ниже может поставить в тупик, поскольку его сложно объяснить. Так что если почувствуете, что запутались, смотрите на картинки чуть ниже, станет понятнее.
Наклон в двух направлениях реализуют по аналогии с деревянной игрой. Один сервопривод прикрепляется к внутренней части игрушки, затем второй сервопривод крепится к внешнему корпусу. Стоит заметить, что в деревянном лабиринте целых три коробки помещены одна в другую, как это можно увидеть на первой картинке поста. Я пользовался той же теорией, но несколько ее упростил. Нижняя часть обеих осей не будет действительно горизонтальной, а значит, лабиринт по сути будет вращаться вокруг условного внутреннего стержня.
Так что мы просто имеем два сервопривода, расположенных под углом 90 градусов относительно друг друга. Первый будет вращать второй. А второй, в свою очередь, будет вращать подставку, прикрепленную к самому лабиринту. Ниже две картинки, иллюстрирующие вышеописанное.
И, чтобы стало еще понятнее, вот как это выглядит в действии:
Чтобы соединить приводы я просто туго стянул их резинкой. Нижний сервопривод я приклеил к старому креплению от привода и закрепил на плоском основании.
Лабиринт:
С моделью лабиринта пришлось немного помучиться, но, так или иначе, модель в конце поста вполне рабочая, её при желании можно распечатать на собственном принтере. Я распечатал его в размере 9 на 9 см, т.к. это самый большой размер, который может распечатать мой Makerbot Thing-O-Matic.
Чтобы создать модель, я первым делом отправился на www.mazegenerator.net и создал там лабиринт 9х9. Затем импортировал результат в SketchUp, отрисовал линии и нарастил стенки. Стенки я сделал высокими настолько, чтобы шарик не вываливался из лабиринта, но и не застревал.
Netduino:
Электронная часть не так уж сложна.
Примечание: общие провода расположены так, чтобы быть более заметными.
Модуль Bluetooth:
В данном случае он присоединяется также, как и в моих предыдущих проектах. Код точно такой же, так что подробности можно почитать здесь:
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
Приводы:
Я начал этот раздел с прямого подключения сервоприводов к разъёму питания Netduino. Понимаю, что вообще-то это не рекомендуется делать, поскольку это приводит к большим затратам энергии, но для запуска концепта это должно быть приемлемо.
Кроме того, у меня возникли другие проблемы, кроме потребляемой мощности – как только стартовали сервоприводы, происходило отключение модуля Bluetooth. Это вполне объяснимо шумом/помехами, но я не сталкивался с тем, чтобы это влияло на какой-либо другой компонент так явно.
Так что я подсоединил их к батарейному блоку на 4.8V. Важно помнить, что необходимо соединить заземление батареи и Netduino. Сигнальные провода идут к контактам ШИМ.
Конечная точка лабиринта:
Чтобы сделать контакты конечной точки лабиринта, я отрезал две металлические полоски и поместил одну горизонтально в конечной точке, а вторую – вертикально, с небольшим зазором относительно первой. Когда шарик достигает конца лабиринта, он замыкает контакт между двумя площадками и активирует выключатель.
Код:
После добавления кода Bluetooth (по ссылкам выше) остальное становится довольно просто.
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();
}
Для начала мы устанавливаем оба сервопривода, Bluetooth и выключатель на контактной площадке конца лабиринта. Чтобы разобраться, как и почему работают выключатели на Netduino, сходите по ссылке. Затем выравниваем сервоприводы, чтобы лабиринт был расположен горизонтально.
В идеальном мире, в котором они держатся не на резинках, оба значения будут 90 градусов. Класс приводов взят здесь.
Код в обработчике событий заставит выключатель срабатывать, когда шарик замкнет контакты. Но, поскольку мы не хотим, чтобы это срабатывало раз за разом, мы убедимся, что текущая игра запущена (с помощью isRunning bool).
В заключительной части кода Netduino обрабатывает сообщения от телефона.
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);
}
}
}
Когда игрок нажимает кнопку GO на телефоне, по каналу Bluetooth посылается команда «start». Так что мы устанавливаем bool в значение true, чтобы показать, что игра началась.
Если сообщение – не сообщение о старте, то мы знаем, что это значения акселерометра. Как будет понятно из телефонной части кода, мы отправляем эти данные как значения по осям X, Y. Код разделяет их, затем конвертирует в значения int и устанавливает сервоприводы. Как упоминалось ранее, я добавляю небольшое смещение, поскольку мои приводы не до конца откалиброваны по уровню.
WP8:
Телефонный код совершенно прост. Вот базовые функции, которые он выполняет:
— Послать «start», когда нажата кнопка GO
— Отобразить таймер
— Отправить значения акселерометра по осям X и Y
— Отобразить окончательное время игры при получении «done»
И вот код для каждой из них:
Послать «start», когда нажата кнопка GO и отобразить таймер
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;
}
Следует отметить, что использовать DispatcherTimer с такой высокой частотой, вероятно, не самая лучшая идея. И он не должен использоваться кроме случаев, когда это действительно необходимо.
Отправить значения акселерометра по осям X и Y
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;
}
Те, кто использовал MathHelper.Clamp в XNA должны узнать функционал. Он просто останавливает значение от превышения или снижения вне установленного лимита. Код внутри Dispatcher делает несколько визуальных действий, чтобы показать угол наклона в UI.
Показ финального времени после получения “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);
}
}
Вот и все, по сути. Смотрите исходники для примеров BT и UI.
Загрузки:
В zip-файле содержатся решение для Netduino, решение для WP8 и STL-модель для распечатывания.
Поделитесь вашими проектами, в которых использовался WP8!