Kinect Swipe Conflict between left hand and right hand - c#

I have written a Kinect swipe testing program from scratch that read gbd files produced by Visual gesture builder Kinect Swipe Test Project here.
The Main 4 gestures I want to focus in this questions is Left & right Hands up, Left & right hand Swipe up.
In the github link i posted above it works fine integrated together.
However, when i port the code over to my main project, Left hand Hands
Up and Right hand Swipe up gestures both malfunctioned. Both gesture
are unable to be detected.
I cannot post my Main project here so i will be posting the code snippets of how i implemented on the 2 Main project files, Mainly GestureDetector.cs and GestureResultsView.cs
Here are the respective snippet of the i had issue with files.
GestureDetector.cs :
public class GestureDetector : IDisposable
{
//stores an array of paths that leads to the gbd files
private string[] databaseArray = {//Left Hand
#"Database\LeftHandDownToTop.gbd"
//, #"Database\LeftHandHandsUp.gbd"
//, #"Database\LeftHandLeftToRight.gbd"
//, #"Database\LeftHandRightToLeft.gbd"
//Right Hand
, #"Database\RightHandBottomToUp.gbd"
, #"Database\RightHandHandsUp.gbd"
//, #"Database\RightHandLeftToRight.gbd"
//, #"Database\RightHandRightToLeft.gbd"
};
//stores an array of the names of the gesture found inside the gbd file
private string[] databaseGestureNameArray = { //Swipe Up
"SwipeUp_Left",
"SwipeUp_Right",
//Swipe Right
//"SwipeRight_Left",
//"SwipeRight_Right",
//Swipe Left
// "SwipeLeft_Left",
// "SwipeLeft_Right",
//Hands Up
//"HandsUp_Left",
"HandsUp_Right"
};
for (int i = 0; i < databaseArray.Length; i++)
{
// load the 'Seated' gesture from the gesture database
using (VisualGestureBuilderDatabase database = new VisualGestureBuilderDatabase(databaseArray[i]))
{
// we could load all available gestures in the database with a call to vgbFrameSource.AddGestures(database.AvailableGestures),
// but for this program, we only want to track one discrete gesture from the database, so we'll load it by name
foreach (Gesture gesture in database.AvailableGestures)
{
if (gesture.Name.Equals(databaseGestureNameArray[i]))
{
this.vgbFrameSource.AddGesture(gesture);
}
}
}
}
}
/// <summary>
/// Handles gesture detection results arriving from the sensor for the associated body tracking Id
/// </summary>
/// <param name="sender">object sending the event</param>
/// <param name="e">event arguments</param>
private void Reader_GestureFrameArrived(object sender, VisualGestureBuilderFrameArrivedEventArgs e)
{
VisualGestureBuilderFrameReference frameReference = e.FrameReference;
using (VisualGestureBuilderFrame frame = frameReference.AcquireFrame())
{
if (frame != null)
{
// get the discrete gesture results which arrived with the latest frame
IReadOnlyDictionary<Gesture, DiscreteGestureResult> discreteResults = frame.DiscreteGestureResults;
bool foundGesture = false;
if (discreteResults != null)
{
// we only have one gesture in this source object, but you can get multiple gestures
foreach (Gesture gesture in this.vgbFrameSource.Gestures)
{
for (int i = 0; i < databaseGestureNameArray.Length; i++)
{
if (gesture.Name.Equals(databaseGestureNameArray[i]) && gesture.GestureType == GestureType.Discrete)
{
DiscreteGestureResult result = null;
discreteResults.TryGetValue(gesture, out result);
if (result != null && !foundGesture)
{
// update the GestureResultView object with new gesture result values & update the label
foundGesture = this.GestureResultView.UpdateGestureResult(true, result.Detected, result.Confidence, databaseGestureNameArray[i]);
}
}
}
}
}
}
}
}
GestureResultView.cs :
public bool UpdateGestureResult(bool isBodyTrackingIdValid, bool isGestureDetected, float detectionConfidence, string gestureName)
{
this.IsTracked = isBodyTrackingIdValid;
this.Confidence = 0.0f;
bool gestureFound = false;
if (!this.IsTracked)
{
this.ImageSource = this.notTrackedImage;
this.Detected = false;
this.BodyColor = Brushes.Gray;
}
else
{
this.Detected = isGestureDetected;
this.BodyColor = this.trackedColors[this.BodyIndex];
if (this.Detected)
{
this.Confidence = detectionConfidence;
if (this.Confidence > 0.4)
{
this.ImageSource = this.seatedImage;
//https://stackoverflow.com/questions/15425495/change-wpf-mainwindow-label-from-another-class-and-separate-thread
//to change label in other class wpf
//http://the--semicolon.blogspot.co.id/p/change-wpf-window-label-content-from.html
string state = App.Current.Properties["state"].ToString();
switch (gestureName)
{
case "SwipeUp_Right":
SwipeUp(state);
break;
case "SwipeUp_Left":
SwipeUp(state);
break;
case "SwipeDown_Right":
break;
case "SwipeDown_Left":
break;
case "SwipeLeft_Right":
break;
case "SwipeLeft_Left":
break;
case "HandsUp_Right":
if (state.Equals("GoBackHome"))
{
}
else
{
Thread.Sleep(350);
MainWindow.handsUp();
}
break;
case "HandsUp_Left":
if (state.Equals("GoBackHome"))
{
}
else
{
Thread.Sleep(350);
MainWindow.handsUp();
}
break;
}
//"HandsUp_Right"
// , "SwipeRight_Right"
// , "SwipeUp_Right"
// , "SwipeLeft_Right"
// , "HandsUp_Left"
// , "SwipeRight_Left"
gestureFound = true;
}
}
else
{
this.ImageSource = this.notSeatedImage;
}
}
return gestureFound;
}
}
//Routing for gesture start
/// <summary>
/// Take in the current screen, filtered swipe up gesture
/// </summary>
/// <param name="state"></param>
private void SwipeUp(string state)
{
if (state.Equals("Home"))
{
int milliseconds = 350;
Thread.Sleep(milliseconds);
//await Task.Delay(500);
Home.swipeUp();
}
else if (state.Equals("ItemChoice"))
{
int milliseconds = 350;
Thread.Sleep(milliseconds);
//await Task.Delay(500);
ItemChoice.swipeUp();
}
else if (state.Equals("ItemParentChoice"))
{
int milliseconds = 350;
Thread.Sleep(milliseconds);
//await Task.Delay(500);
ItemParentChoice.swipeUp();
}
else if (state.Equals("LoanSummary"))
{
int milliseconds = 350;
Thread.Sleep(milliseconds);
//await Task.Delay(500);
LoanSummary.swipeUp();
}
else if (state.Equals("Timeslot"))
{
int milliseconds = 350;
Thread.Sleep(milliseconds);
//await Task.Delay(500);
Timeslot.swipeUp();
}
else if (state.Equals("ViewLoans"))
{
int milliseconds = 350;
Thread.Sleep(milliseconds);
//await Task.Delay(500);
ViewLoans.swipeUp();
}
else if (state.Equals("WhatToDo"))
{
int milliseconds = 350;
Thread.Sleep(milliseconds);
//await Task.Delay(500);
//WhatToDo.swipeUp();
}
}
/// <summary>
/// Take in the current screen, filtered swipe right gesture
/// </summary>
/// <param name="state"></param>
private void swipeRight(string state)
{
if (state.Equals("Home"))
{
//int milliseconds = 500;
//Thread.Sleep(milliseconds);
//Home.swipeUp();
}
else if (state.Equals("ItemChoice"))
{
int milliseconds = 500;
Thread.Sleep(milliseconds);
ItemChoice.swipeRight();
}
else if (state.Equals("ItemParentChoice"))
{
int milliseconds = 500;
Thread.Sleep(milliseconds);
ItemParentChoice.swipeRight();
}
else if (state.Equals("LoanSummary"))
{
//int milliseconds = 500;
//Thread.Sleep(milliseconds);
//LoanSummary.swipeUp();
}
else if (state.Equals("Timeslot"))
{
int milliseconds = 500;
Thread.Sleep(milliseconds);
Timeslot.swipeRight();
}
else if (state.Equals("ViewLoans"))
{
//int milliseconds = 500;
//Thread.Sleep(milliseconds);
//ViewLoans.swipeUp();
}
else if (state.Equals("WhatToDo"))
{
//int milliseconds = 500;
//Thread.Sleep(milliseconds);
//Home.swipeUp();
}
}
/// <summary>
/// Take in the current screen, filtered swipe right gesture
/// </summary>
/// <param name="state"></param>
private void swipeLeft(string state)
{
if (state.Equals("Home"))
{
//int milliseconds = 500;
//Thread.Sleep(milliseconds);
//Home.swipeUp();
}
else if (state.Equals("ItemChoice"))
{
int milliseconds = 500;
Thread.Sleep(milliseconds);
ItemChoice.swipeLeft();
}
else if (state.Equals("ItemParentChoice"))
{
int milliseconds = 500;
Thread.Sleep(milliseconds);
ItemParentChoice.swipeLeft();
}
else if (state.Equals("LoanSummary"))
{
//int milliseconds = 500;
//Thread.Sleep(milliseconds);
//LoanSummary.swipeUp();
}
else if (state.Equals("Timeslot"))
{
int milliseconds = 500;
Thread.Sleep(milliseconds);
Timeslot.swipeLeft();
}
else if (state.Equals("ViewLoans"))
{
//int milliseconds = 500;
//Thread.Sleep(milliseconds);
//ViewLoans.swipeUp();
}
else if (state.Equals("WhatToDo"))
{
//int milliseconds = 500;
//Thread.Sleep(milliseconds);
//Home.swipeUp();
}
}
//routing for gesture end
}

Related

How to make image zoom in & out with mouse wheel in Blazor?

I want to zoom in & out an image in blazor on asp.net.
As I use for Google Maps, I want to move the image position by zooming and dragging the image with the mouse wheel.(I want to use an image file, not a Google map.)
Is there a way to zoom in, zoom out and drag specific images in blazor?
Note:
I would only use this on BlazorWasm and not BlazorServer because there might be quite a bit of lag if the network is slow.
It is probably easier to just use JavaScript and/or JavaScript interoperability (JS interop) but for this example I decided not to use JS Interop.
This component enables you to zoom by pressing shift while mouse wheel up (zoom out) or mouse wheel down (zoom in) and move the image by pressing mouse button 1 down while moving. (it's more panning than dragging it)
Restriction in Blazor: (at the time of writing this)
The biggest issue at the moment is not having access to the mouse OffsetX and OffsetY within the html element as described here and also here, so the moving of the image has to be done using CSS only.
The reason I used Shift for scrolling is because scrolling is not being blocked or disabled as described here even with #onscroll:stopPropagation, #onwheel:stopPropagation, #onmousewheel:stopPropagation and/or #onscroll:preventDefault, #onwheel:preventDefault, #onmousewheel:preventDefault set on the parent mainImageContainer element. The screen will still scroll left and right if the content is wider than the viewable page.
Solution:
The Zooming part is pretty straight forward, all you need to do is set the transform:scale(n); property in the #onmousewheel event.
The moving of the image is a bit more complex because there is no reference to where the mouse pointer is in relation to the image or element boundaries. (OffsetX and OffsetY)
The only thing we can determine is if a mouse button is pressed and then calculate what direction the mouse is moving in left, right, up or down.
The idea is then to move the position of element with the image by setting the top and left CSS values as percentages.
This component code:
#using System.Text;
<div id="mainImageContainer" style="display: block;width:#($"{ImageWidthInPx}px");height:#($"{ImageHeightInPx}px");overflow: hidden;">
<div id="imageMover"
#onmousewheel="MouseWheelZooming"
style="#MoveImageStyle">
<div id="imageContainer"
#onmousemove="MouseMoving"
style="#ZoomImageStyle">
#*this div is used just for moving around when zoomed*#
</div>
</div>
</div>
#if (ShowResetButton)
{
<div style="display:block">
<button #onclick="ResetImgage">Reset</button>
</div>
}
#code{
/// <summary>
/// The path or url of the image
/// </summary>
[Parameter]
public string ImageUrlPath { get; set; }
/// <summary>
/// The width of the image
/// </summary>
[Parameter]
public int ImageWidthInPx { get; set; }
/// <summary>
/// The height of the image
/// </summary>
[Parameter]
public int ImageHeightInPx { get; set; }
/// <summary>
/// Set to true to show the reset button
/// </summary>
[Parameter]
public bool ShowResetButton { get; set; }
/// <summary>
/// Set the amount the image is scaled by, default is 0.1f
/// </summary>
[Parameter]
public double DefaultScaleBy { get; set; } = 0.1f;
/// <summary>
/// The Maximum the image can scale to, default = 5f
/// </summary>
[Parameter]
public double ScaleToMaximum { get; set; } = 5f;
/// <summary>
/// Set the speed at which the image is moved by, default 2.
/// 2 or 3 seems to work best.
/// </summary>
[Parameter]
public double DefaultMoveBy { get; set; } = 2;
//defaults
double _CurrentScale = 1.0f;
double _PositionLeft = 0;
double _PositionTop = 0;
double _OldClientX = 0;
double _OldClientY = 0;
double _DefaultMinPosition = 0;//to the top and left
double _DefaultMaxPosition = 0;//to the right and down
//the default settings used to display the image in the child div
private Dictionary<string, string> _ImageContainerStyles;
Dictionary<string, string> ImageContainerStyles
{
get
{
if (_ImageContainerStyles == null)
{
_ImageContainerStyles = new Dictionary<string, string>();
_ImageContainerStyles.Add("width", "100%");
_ImageContainerStyles.Add("height", "100%");
_ImageContainerStyles.Add("position", "relative");
_ImageContainerStyles.Add("background-size", "contain");
_ImageContainerStyles.Add("background-repeat", "no-repeat");
_ImageContainerStyles.Add("background-position", "50% 50%");
_ImageContainerStyles.Add("background-image", $"URL({ImageUrlPath})");
}
return _ImageContainerStyles;
}
}
private Dictionary<string, string> _MovingContainerStyles;
Dictionary<string, string> MovingContainerStyles
{
get
{
if (_MovingContainerStyles == null)
{
InvokeAsync(ResetImgage);
}
return _MovingContainerStyles;
}
}
protected async Task ResetImgage()
{
_PositionLeft = 0;
_PositionTop = 0;
_DefaultMinPosition = 0;
_DefaultMaxPosition = 0;
_CurrentScale = 1.0f;
_MovingContainerStyles = new Dictionary<string, string>();
_MovingContainerStyles.Add("width", "100%");
_MovingContainerStyles.Add("height", "100%");
_MovingContainerStyles.Add("position", "relative");
_MovingContainerStyles.Add("left", $"{_PositionLeft}%");
_MovingContainerStyles.TryAdd("top", $"{_PositionTop}%");
await InvokeAsync(StateHasChanged);
}
string ZoomImageStyle { get => DictionaryToCss(ImageContainerStyles); }
string MoveImageStyle { get => DictionaryToCss(MovingContainerStyles); }
private string DictionaryToCss(Dictionary<string, string> styleDictionary)
{
StringBuilder sb = new StringBuilder();
foreach (var kvp in styleDictionary.AsEnumerable())
{
sb.AppendFormat("{0}:{1};", kvp.Key, kvp.Value);
}
return sb.ToString();
}
protected async void MouseMoving(MouseEventArgs e)
{
//if the mouse button 1 is not down exit the function
if (e.Buttons != 1)
{
_OldClientX = e.ClientX;
_OldClientY = e.ClientY;
return;
}
//get the % of the current scale to move by at least the default move speed plus any scaled changes
//basically the bigger the image the faster it moves..
double scaleFrac = (_CurrentScale / ScaleToMaximum);
double scaleMove = (DefaultMoveBy * (DefaultMoveBy * scaleFrac));
//moving mouse right
if (_OldClientX < e.ClientX)
{
if ((_PositionLeft - DefaultMoveBy) <= _DefaultMaxPosition)
{
_PositionLeft += scaleMove;
}
}
//moving mouse left
if (_OldClientX > e.ClientX)
{
//if (_DefaultMinPosition < (_PositionLeft - DefaultMoveBy))
if ((_PositionLeft + DefaultMoveBy) >= _DefaultMinPosition)
{
_PositionLeft -= scaleMove;
}
}
//moving mouse down
if (_OldClientY < e.ClientY)
{
//if ((_PositionTop + DefaultMoveBy) <= _DefaultMaxPosition)
if ((_PositionTop - DefaultMoveBy) <= _DefaultMaxPosition)
{
_PositionTop += scaleMove;
}
}
//moving mouse up
if (_OldClientY > e.ClientY)
{
//if ((_PositionTop - DefaultMoveBy) > _DefaultMinPosition)
if ((_PositionTop + DefaultMoveBy) >= _DefaultMinPosition)
{
_PositionTop -= scaleMove;
}
}
_OldClientX = e.ClientX;
_OldClientY = e.ClientY;
await UpdateScaleAndPosition();
}
async Task<double> IncreaseScale()
{
return await Task.Run(() =>
{
//increase the scale first then calculate the max and min positions
_CurrentScale += DefaultScaleBy;
double scaleFrac = (_CurrentScale / ScaleToMaximum);
double scaleDiff = (DefaultMoveBy + (DefaultMoveBy * scaleFrac));
double scaleChange = DefaultMoveBy + scaleDiff;
_DefaultMaxPosition += scaleChange;
_DefaultMinPosition -= scaleChange;
return _CurrentScale;
});
}
async Task<double> DecreaseScale()
{
return await Task.Run(() =>
{
_CurrentScale -= DefaultScaleBy;
double scaleFrac = (_CurrentScale / ScaleToMaximum);
double scaleDiff = (DefaultMoveBy + (DefaultMoveBy * scaleFrac));
double scaleChange = DefaultMoveBy + scaleDiff;
_DefaultMaxPosition -= scaleChange;
_DefaultMinPosition += scaleChange;//DefaultMoveBy;
//fix descaling, move the image back into view when descaling (zoomin out)
if (_CurrentScale <= 1)
{
_PositionLeft = 0;
_PositionTop = 0;
}
else
{
//left can not be more than max position
_PositionLeft = (_DefaultMaxPosition < _PositionLeft) ? _DefaultMaxPosition : _PositionLeft;
//top can not be more than max position
_PositionTop = (_DefaultMaxPosition < _PositionTop) ? _DefaultMaxPosition : _PositionTop;
//left can not be less than min position
_PositionLeft = (_DefaultMinPosition > _PositionLeft) ? _DefaultMinPosition : _PositionLeft;
//top can not be less than min position
_PositionTop = (_DefaultMinPosition > _PositionTop) ? _DefaultMinPosition : _PositionTop;
}
return _CurrentScale;
});
}
protected async void MouseWheelZooming(WheelEventArgs e)
{
//holding shift stops the page from scrolling
if (e.ShiftKey == true)
{
if (e.DeltaY > 0)
{
_CurrentScale = ((_CurrentScale + DefaultScaleBy) >= 5) ? _CurrentScale = 5f : await IncreaseScale();
}
if (e.DeltaY < 0)
{
_CurrentScale = ((_CurrentScale - DefaultScaleBy) <= 0) ? _CurrentScale = DefaultScaleBy : await DecreaseScale();
}
await UpdateScaleAndPosition();
}
}
/// <summary>
/// Refresh the values in the moving style dictionary that is used to position the image.
/// </summary>
async Task UpdateScaleAndPosition()
{
await Task.Run(() =>
{
if (!MovingContainerStyles.TryAdd("transform", $"scale({_CurrentScale})"))
{
MovingContainerStyles["transform"] = $"scale({_CurrentScale})";
}
if (!MovingContainerStyles.TryAdd("left", $"{_PositionLeft}%"))
{
MovingContainerStyles["left"] = $"{_PositionLeft}%";
}
if (!MovingContainerStyles.TryAdd("top", $"{_PositionTop}%"))
{
MovingContainerStyles["top"] = $"{_PositionTop}%";
}
});
}
}
This is the usage:
#page "/"
#using BlazorWasmApp.Components
Welcome to your new app.
<ZoomableImageComponent ImageUrlPath="images/Capricorn.png"
ImageWidthInPx=400
ImageHeightInPx=300
ShowResetButton=true
DefaultScaleBy=0.1f />
and this is the result:
I only tested this in chrome on a desktop computer without touch input.

Continuously getting data from heart rate monitor

I've been working on a project that reads a person's heart rate from a Polar H7 hrm. I've been successful in connecting the device and getting the heart rate which the program shows as text in the UI. However, there are instances where the program suddenly stops getting input from the device.
I have already checked the connection of the device to my Win 10 laptop and saw that it was stable, there were also no exceptions getting thrown by the program. The text simply stops changing.
Here is the code I've written:
public sealed partial class MainPage : Page
{
private GattDeviceService device;
public MainPage()
{
this.InitializeComponent();
init();
}
async void init()
{
var devices = await DeviceInformation.FindAllAsync(GattDeviceService.GetDeviceSelectorFromUuid(GattServiceUuids.HeartRate));
Status.Text = devices.Count.ToString();
device = await GattDeviceService.FromIdAsync(devices[0].Id);
enableSensor();
}
private async void enableSensor()
{
IReadOnlyList<GattCharacteristic> characteristicList;
characteristicList = device.GetAllCharacteristics();
if (characteristicList != null)
{
GattCharacteristic characteristic = characteristicList[0];
if (characteristic.CharacteristicProperties.HasFlag(GattCharacteristicProperties.Notify))
{
characteristic.ValueChanged += SendNotif;
await characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify);
}
}
}
async void SendNotif(GattCharacteristic sender, GattValueChangedEventArgs eventArgs)
{
if (eventArgs.CharacteristicValue.Length != 0) {
byte[] hrData = new byte[eventArgs.CharacteristicValue.Length];
DataReader.FromBuffer(eventArgs.CharacteristicValue).ReadBytes(hrData);
var hrValue = ProcessData(hrData);
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
Status.Text = hrValue.ToString();
});
}
}
private int ProcessData(byte[] data)
{
// Heart Rate profile defined flag values
const byte heartRateValueFormat = 0x01;
byte currentOffset = 0;
byte flags = data[currentOffset];
bool isHeartRateValueSizeLong = ((flags & heartRateValueFormat) != 0);
currentOffset++;
ushort heartRateMeasurementValue;
if (isHeartRateValueSizeLong)
{
heartRateMeasurementValue = (ushort)((data[currentOffset + 1] << 8) + data[currentOffset]);
currentOffset += 2;
}
else
{
heartRateMeasurementValue = data[currentOffset];
}
return heartRateMeasurementValue;
}
}

Check if multiple buttons are pushed Windows IoT with Raspberry PI 3B

I'm currently working with the Raspberry PI 3 with Windows 10 IoT and I'm trying to make the game 'Simon Says' in Visual Studio 2015.
Everything is plugged in as followed:
Fritzing image
I got the LEDs working but I can't really figure out how to detect when a button is being pressed.
Parts of the source code:
Variables
private const int BTNRED = 5;
private const int BTNBLUE = 6;
private const int BTNYELLOW = 13;
private const int BTNGREEN = 19;
private GpioPin _btnRed;
private GpioPin _btnBlue;
private GpioPin _btnYellow;
private GpioPin _btnGreen;
private Random rnd;
private static GpioController _gpioController;
Method to recognize when a button is being pressed.
public static IList<Step> PressedButtons
{
get
{
List<Step> returnVal = new List<Step>();
if (GetMap._btnBlue.Read() == GpioPinValue.High)
{ returnVal.Add(Step.Blue); }
if (GetMap._btnGreen.Read() == GpioPinValue.High)
{ returnVal.Add(Step.Green); }
if (GetMap._btnRed.Read() == GpioPinValue.High)
{ returnVal.Add(Step.Red); }
if (GetMap._btnYellow.Read() == GpioPinValue.High)
{ returnVal.Add(Step.Yellow); }
return returnVal;
}
}
/// <summary>
/// Checks if any of the 4 buttons is pressed.
/// </summary>
/// <returns>Returns false if no button is pressed.</returns>
public static bool isButtonPressed
{
get
{
if (GetMap._btnBlue.Read() == GpioPinValue.Low)
{ return true; }
if (GetMap._btnGreen.Read() == GpioPinValue.Low)
{ return true; }
if (GetMap._btnRed.Read() == GpioPinValue.Low)
{ return true; }
if (GetMap._btnYellow.Read() == GpioPinValue.Low)
{ return true; ; }
return false;
}
}
Assigning the right pins on the GPIO to the corresponding variables
private GPIOMap()
{
rnd = new Random();
_gpioController = GpioController.GetDefault();
if (_gpioController == null) { return; }
_ledRed = _gpioController.OpenPin(LEDRED);
_ledRed.SetDriveMode(GpioPinDriveMode.Output);
_ledYellow = _gpioController.OpenPin(LEDYELLOW);
_ledYellow.SetDriveMode(GpioPinDriveMode.Output);
_ledGreen = _gpioController.OpenPin(LEDGREEN);
_ledGreen.SetDriveMode(GpioPinDriveMode.Output);
_ledBlue = _gpioController.OpenPin(LEDBLUE);
_ledBlue.SetDriveMode(GpioPinDriveMode.Output);
_btnBlue = _gpioController.OpenPin(BTNBLUE);
_btnGreen = _gpioController.OpenPin(BTNGREEN);
_btnRed = _gpioController.OpenPin(BTNRED);
_btnYellow = _gpioController.OpenPin(BTNYELLOW);
}
private static GPIOMap GPIOMapInstance;
public static GPIOMap GetMap
{
get
{
if (GPIOMapInstance == null || _gpioController == null)
{ GPIOMapInstance = new GPIOMap(); }
return GPIOMapInstance;
}
}
Run game, where as you can see it should bring me to state 2 whenever a button is being pressed. However this isn't quite the case as the idle animation keeps on going on.
private void RunGame()
{
while (_isRunning)
{
switch (_state)
{
//Idle state
case (0x01):
//If any button is pressed, start the game.
if (GPIOMap.isButtonPressed)
{
_state = 0x02;
Task.Delay(500).Wait();
GPIOMap.PlayStartAnimation();
Task.Delay(1000).Wait();
break;
}
//If nothing happens, continue playing the idle animation.
GPIOMap.PlayIdleAnimationOnce();
break;
//Playback state
case (0x02):
//Play all the currently existing steps.
for (int i = 0; i <= _gameInstance.TotalSteps; i++)
{ GPIOMap.BlinkLight(_gameInstance.GetNextStep(), 500); }
//Generate and play a new step.
GPIOMap.BlinkLight(_gameInstance.GetNextStep(), 500);
//Switch to the input state.
_state = 0x03;
//Set the start time for the input.
_answerPending = DateTime.UtcNow;
break;
//Input state
case (0x03):
if(_gameInstance.CurrentStep > MAXSTEPS)
{
//Win Game
ResetGame();
break;
}
//If the player waits too long before answering, lose the game.
if ((DateTime.UtcNow - _answerPending) > _timeout)
{
GPIOMap.ConfirmWrongAnswer();
ResetGame();
break;
}
//If there are no more steps to re-trace, go back to playback state.
if (_gameInstance.CurrentStep > _gameInstance.TotalSteps)
{ _gameInstance.ResetSteps(); _state = 0x02; break; }
//If no buttons are pressed, keep polling.
if (!GPIOMap.isButtonPressed) { break; }
IList<Step> pressedButtons = GPIOMap.PressedButtons;
//If anything but one button is pressed, lose the game.
if (pressedButtons.Count != 1)
{
GPIOMap.ConfirmWrongAnswer();
ResetGame();
break;
}
if (_gameInstance.VerifyStep(pressedButtons[0]))
{
GPIOMap.BlinkLight(pressedButtons[0], 500);
}
else//Wrong step, end game
{
GPIOMap.ConfirmWrongAnswer();
ResetGame();
break;
}
break;
}
}
}
How can I get my source code to recognize when a button is being pressed so I can go to the next state which involves getting user input on the buttons.
You can use GPIO ValueChanged to catch the button pressed event like this:
_btnBlue = _gpioController.OpenPin(BTNBLUE);
_btnBlue.ValueChanged += btnValueChanged;
public void btnValueChanged(GpioPin pin, GpioPinValueChangedEventArgs e)
{
if (_btnBlue.Read() == GpioPinValue.Low)
{
isBlueButtonPressed = true;
}
}

Serial Port - reading data/updating canvas element

I've been having issues for a few weeks now reading in data from a serial port and updating a canvas element based on the message being received. The canvas element to be updated is a small image, that is supposed to rotate to a certain angle based on the third section of the message being received. I'm not sure what's going wrong, it seems that a full message isn't always being received. I'm going to give as much detail about the port and data.
Structure - 8 data bits, 1 start bit, 1 stop bit, no parity. Message frequency is 15 Hz (the number of lines written every second). The default baud rate is 9,600.
There are five sections:
1. Section1 - two decimal places.
2. Section2 - two decimal places.
3. Angle - multiplied by 10 – i.e. 256.0 degrees is shown as 2560.
4. Section4 multiplied by -100 – i.e. 6.66 degrees is -666.
5. Section5 multiplied by 100 – i.e. 55.5 degrees is 555.
A message starts with a colon symbol (:) and ends with < CR>< LF> . A field that contains asterisks, '***', indicates that no value is defined for that field.
An example to illustrate:
Column
1 15 22 29 36 43
1.00 *** 0 0 0
: 1.00 20.20 2460 0 0
: 2.40 20.45 2460 10000 -10000
: 3.00 20.45 2355 1000 554
I have the latest message received being shown at the top of the window to ensure the user that data is being received, but I noticed the message is only show bits and pieces of what it should be, and thus messing up the rotation of the canvas element. So for instance, the message might be : 20 500 at first, and then get a complete message.
Here's a screenshot of the data being sent:
Here's relative code in my MainWindow.cs:
private Port port = new Port();
private double rovHeading;
Point rotate_Origin = new Point(0.5, 0.5);
public string LastCOMMessage
{
get { return (string)this.GetValue(LastCoMMessageProperty); }
set { this.SetValue(LastCoMMessageProperty, value); }
}
private void Connect_Port(object sender, RoutedEventArgs e)
{
if (port.Status == Port.PortStatus.Disconnected)
{
try
{
port.Connect("COM1", 9600, false, 250); //When set to false, test data is used on Connect. Last parameter is how often the UI is updated. Closer to 0, the faster it updates.
//Sets button State and Creates function call on data recieved
Connect_btn.Content = "Disconnect";
port.OnMessageReceived += new Port.MessageReceivedHandler(port_OnMessageReceived);
}
catch (Exception)
{
System.Windows.MessageBox.Show("Port is not available.");
}
}
else
{
try // just in case serial port is not open, could also be achieved using if(serial.IsOpen)
{
port.Disconnect();
Connect_btn.Content = "Connect";
}
catch
{
}
}
}
/// <summary>
/// Reads in the data streaming from the port, taking out the third value which is the ROV's heading and assigning it to rovHeading.
/// It then calls orientRovImage.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void port_OnMessageReceived(object sender, Port.MessageEventArgs e)
{
LastCOMMessage = e.Message;
if (rovImage != null && e.Message.EndsWith("\r\n")) //messages end in <CR><LF>. Was having issues where only half a message would be received and updated the heading to wrong number.
{
string[] messageComponents = LastCOMMessage.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
if (messageComponents.Length >= 3)
{
double.TryParse(messageComponents[3], out rovHeading);
if (rovHeading > 0)
{
rovHeading /= 10;
orientRovImage();
}
}
}
}
/// <summary>
/// Rotates the ROV icon based on the heading being streamed in.
/// </summary>
private void orientRovImage()
{
RotateTransform rotateRov = new RotateTransform(rovHeading + angle_Offset);
rovImage.RenderTransformOrigin = rotate_Origin;
rovImage.RenderTransform = rotateRov;
}
Here's my Port.cs:
class Port
{
private SerialPort serial = null;
private DispatcherTimer testTimer;
private string[] testData = new string[] { ": 3.00 20.45 2355 1000 554\r\n", ": 5.00 78.09 1725 3200 121\r\n", ": 9.20 10.12 1492 8820 197\r\n" }; //test data to be sent when liveData is set to false.
private int testDataIndex = -1;
private DateTime dateOflastMessageHandled;
public enum PortStatus
{
Connected,
Disconnected
}
public PortStatus Status { get; private set; }
public bool LiveData { get; private set; }
public int MillisecondsDelayBetweenMessages { get; private set; }
public class MessageEventArgs : EventArgs
{
public string Message { get; private set; }
public MessageEventArgs(string message)
{
Message = message;
}
}
public delegate void MessageReceivedHandler(object sender, MessageEventArgs e);
public event MessageReceivedHandler OnMessageReceived;
private void MessageReceived(string message)
{
if (OnMessageReceived == null)
{
return;
}
OnMessageReceived(this, new MessageEventArgs(message));
}
public Port()
{
Status = PortStatus.Disconnected;
}
public void Connect(string portName, int baudRate, bool liveData, int millisecondsDelayBetweenMessages)
{
LiveData = liveData;
MillisecondsDelayBetweenMessages = millisecondsDelayBetweenMessages;
Disconnect();
if (liveData)
{
serial = new SerialPort();
serial.PortName = portName;
serial.BaudRate = baudRate;
serial.Handshake = Handshake.None;
serial.Parity = Parity.None;
serial.DataBits = 8;
serial.StopBits = StopBits.One;
serial.ReadTimeout = 200;
serial.WriteTimeout = 50;
serial.Open();
serial.DataReceived += new SerialDataReceivedEventHandler(Receive);
}
else
{
testTimer = new DispatcherTimer();
testTimer.Interval = new TimeSpan(0, 0, 0, 0, 3);
testTimer.Tick += new EventHandler(testTimer_Tick);
testTimer.Start();
}
Status = PortStatus.Connected;
}
private void testTimer_Tick(object sender, EventArgs e)
{
if (dateOflastMessageHandled == null || DateTime.Now.Subtract(dateOflastMessageHandled).TotalMilliseconds >= MillisecondsDelayBetweenMessages)
{
dateOflastMessageHandled = DateTime.Now;
MessageReceived(testData[testDataIndex]);
testDataIndex++;
if (testDataIndex >= testData.Length)
{
testDataIndex = 0;
}
}
}
private void Receive(object sender, SerialDataReceivedEventArgs e)
{
if (dateOflastMessageHandled == null || DateTime.Now.Subtract(dateOflastMessageHandled).TotalMilliseconds >= MillisecondsDelayBetweenMessages)
{
dateOflastMessageHandled = DateTime.Now;
Application.Current.Dispatcher.BeginInvoke(new Action(
delegate()
{
//MessageReceived(serial.ReadLine());
MessageReceived(serial.ReadExisting());
}));
}
}
public void Disconnect()
{
if (testTimer != null)
{
testTimer.Stop();
}
testDataIndex = 0;
Status = PortStatus.Disconnected;
if (serial != null)
{
serial.Close();
}
}
}
And finally in my MainWindow.xaml, this just shows the last message received:
<Button Content="Connect" Click="Connect_Port" Name="Connect_btn" />
<TextBlock Text="{Binding LastCOMMessage, ElementName=this}" />
Any hints or help with this would be much appreciated, as this is the first time I've worked w/ ports. Thanks!

Animate opacity over time in XNA

I would like to animate the opacity value of a text string containing the name of the level in and out with a delay in the middle.
So the sequence of events would be like:
Start transparent
Fade in to solid white over a second of game time
Wait a second
Fade out to transparent again over a second.
The code I have written to animate the alpha value isn't working. Plus, it's pretty ugly and I'm sure there's a better way to do it using the XNA framework.
I've been unable to find any advice elsewhere about doing this. Surely animating values like this isn't that uncommon. How can I do it?
Here's my current code as requested (yes it's horrible).
private int fadeStringDirection = +1;
private int fadeStringDuration = 1000;
private float stringAlpha = 0;
private int stringRef = 0;
private int stringPhase = 1;
...
if (!pause)
{
totalMillisecondsElapsed += gameTime.ElapsedGameTime.Milliseconds;
if (fadestringDirection != 0)
{
stringAlpha = ((float)(totalMillisecondsElapsed - stringRef) / (float)(fadeStringDuration*stringPhase)) * fadeStringDirection;
stringAlpha = MathHelper.Clamp(stringAlpha, 0, 1);
if (topAlpha / 2 + 0.5 == fadeStringDirection)
{
fadeStringDirection = 0;
stringRef = totalMillisecondsElapsed;
stringPhase++;
}
}
else
{
stringRef += gameTime.ElapsedGameTime.Milliseconds;
if (stringRef >= fadeStringDuration * stringPhase)
{
stringPhase++;
fadeStringDirection = -1;
stringRef = totalMillisecondsElapsed;
}
}
}
Here's the solution I have now. Much nicer than what I had before (and in a class of its own).
/// <summary>
/// Animation helper class.
/// </summary>
public class Animation
{
List<Keyframe> keyframes = new List<Keyframe>();
int timeline;
int lastFrame = 0;
bool run = false;
int currentIndex;
/// <summary>
/// Construct new animation helper.
/// </summary>
public Animation()
{
}
public void AddKeyframe(int time, float value)
{
Keyframe k = new Keyframe();
k.time = time;
k.value = value;
keyframes.Add(k);
keyframes.Sort(delegate(Keyframe a, Keyframe b) { return a.time.CompareTo(b.time); });
lastFrame = (time > lastFrame) ? time : lastFrame;
}
public void Start()
{
timeline = 0;
currentIndex = 0;
run = true;
}
public void Update(GameTime gameTime, ref float value)
{
if (run)
{
timeline += gameTime.ElapsedGameTime.Milliseconds;
value = MathHelper.SmoothStep(keyframes[currentIndex].value, keyframes[currentIndex + 1].value, (float)timeline / (float)keyframes[currentIndex + 1].time);
if (timeline >= keyframes[currentIndex + 1].time && currentIndex != keyframes.Count) { currentIndex++; }
if (timeline >= lastFrame) { run = false; }
}
}
public struct Keyframe
{
public int time;
public float value;
}
}

Categories