It seems exactly like Win10 IoT - RaspBerry Pi2: ValueChanged not called when GPIO change
I have a raspberry pi 2 with win10 IoT (creator version) and have this C# code:
public sealed class StartupTask : IBackgroundTask
{
private const int SENSOR_PIN = 17;
private GpioPin pinSensor;
public void Run(IBackgroundTaskInstance taskInstance)
{
taskInstance.Canceled += TaskInstance_Canceled; // "destructor"
var gpio = GpioController.GetDefault();
if (gpio != null)
{
pinSensor = gpio.OpenPin(SENSOR_PIN); // also tried with GpioSharingMode.SharedReadOnly
var r = pinSensor.Read(); // works and changes if sensor changes. Verified with quickwatch
pinSensor.SetDriveMode(GpioPinDriveMode.Input);
pinSensor.DebounceTimeout = TimeSpan.FromMilliseconds(20);
pinSensor.ValueChanged += PinIn_ValueChanged;
}
}
private void PinIn_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args)
{
// never gets hit...
}
private void TaskInstance_Canceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
pinSensor.Dispose();
}
}
led on sensor and quickwatch say the GpioPinValue does alternate between high and low... so should get hit...
When I retrieve the drive mode after setting it to input. It tells me it actually is set to input:
var dm = pinSensor.GetDriveMode();
as was suggested in the comment of the linked stack overflow issue. So what am I doing wrong? And more important: why?
When the Run method ends, unless a deferral object is created, the
Background Application ends. The common practice, for asynchronous
programming is to take a deferral like this:
var deferval = taskInstance.GetDeferral();
Ref:Developing Background Applications
Related
When you close the app by swiping it in recent apps, it will cancel any services and terminate most aspects of the app gracefully. However, if there are any notifications that were SetOngoing(true), then these will remain if the app suddenly is closed, and there aren't any services that listen for the app's termination.
What is the right way to deal with this problem?
Recently, I coded a music player, and I arranged it such that in the OnStop for my activities, the notification is canceled (and so is the thread updating the progress bar within it). Then, OnResume, I trigger the notification again.
If they "recent apps swipe" it away, or click away, the notification goes away now, as long as the music isn't playing. So to get rid of the notification, you have to pause it, and then swipe away. Otherwise, there is a leak memory if the app is closed by swipe, where the notification remains open and is buggy afterwards if the app is reopened, and the app crashes if you click the notification (though maybe that's because I can't figure out how to get started with saved state bundles). Likewise, there is a problem if you let the app close the notification every OnStop, as then it will be closed as the user does other things with their phone, even though the music is playing (which sort of defeats the point of it right?)
Are there other better ways to handle this? Who has a good saved state bundle if that is indeed relevant to my issue?
Thanks for the discussion
You can cancel the notification when android App is closed by swipe with the following code:
[Service]
public class ForegroundServiceDemo : Service
{
public override void OnTaskRemoved(Intent rootIntent)
{
//this.StopSelf();
//this.StopForeground(StopForegroundFlags.Remove);
this.StopService(new Intent(this,typeof(ForegroundServiceDemo)));
base.OnTaskRemoved(rootIntent);
}
}
By overriding the OnTaskRemoved method of the service, the system will call this method when user closes the app by swipe. And each of the three lines code can cancel the notification and stop the service when the app is closed by swipe.
I found this, finally, after trying every search terms imaginable, and wow there is a whole section on this. I do not have it working yet, but I can report back with code when I do. Here is the solution: https://developer.android.com/guide/topics/media-apps/media-apps-overview
Seems you have to implement the media player service as a specific kind of service that registers to the notification. I am in the process of refactoring the heart of my code, which perhaps should be terrifying, but feels more like the final algorithm on a Rubix's cube... I will report back in like 10 work hours with some working code (I hope).
Thanks to everyone contributing on this discussion!
OK, so, after much dabbling and dozens of work hours... I have found the best way to handle this issue is to create a MediaBrowserService with a MediaSession. In the notification creation code, it is very particular about how you start that notification (which has to be in the Foreground and bound to the MediaSession). Once this is done, the notification will stay open, even if you close the app, and clicking it will always bring you back to the activity bound to the service (see the supplied code below). Then, you just have a button on the notification to close itself and the app. Voila, a notification that does NOT remain open if the app is closed from the recent apps, etc.
public static void CancelNotificationBreadCrumb()
{
if (cts != null)
{
cts.Cancel();
Thread.Sleep(250);
// Cancellation should have happened, so call Dispose
cts.Dispose();
MyLogger.Debug("MyMediaPlayer: CloseEntireApp: Notification should have been disposed.");
}
}
public static void NotificationNowPlayingBreadCrumb()
{
try
{
Intent intent = MenuManager.GetGoToNowPlayingIntent(context, GetCurrentlyPlaying());
manager = (NotificationManager)context.GetSystemService(NotificationService);
PendingIntent pendingIntent = PendingIntent.GetActivity(context, 1, intent, PendingIntentFlags.Immutable);
NotificationChannel notificationChannel = new NotificationChannel(ChannelId, ChannelId, NotificationImportance.Low);
notificationChannel.EnableLights(false);
notificationChannel.EnableVibration(false);
notificationChannel.SetSound(null, null);
//notificationChannel.SetVibrationPattern(new long[] { 10, 20 });
manager.CreateNotificationChannel(notificationChannel);
Notification notification = NowPlayingAdapter.InflateNotification(context, currentFile, ChannelId, pendingIntent);
service.StartForeground(MY_MEDIA_NOTIFICATION_ID, notification);
manager.Notify(MY_MEDIA_NOTIFICATION_ID, notification);
// Then trigger the thread to update the real-time features
if (cts == null || cts.IsCancellationRequested)
cts = new CancellationTokenSource();
ThreadPool.QueueUserWorkItem(new WaitCallback(RunInBackground), cts.Token);
} catch(Exception e)
{
string message = "MyMediaPlayer: NotificationNowPlayingBreadCrumb: Could not create now playing breadcrumb notification; message: " + e.Message;
MyLogger.Error(message);
}
}
public static void CloseEntireApp()
{
MyLogger.Trace("MyMediaPlayer: Entering CloseEntireApp...");
if (player != null)
player.Release();
CancelNotificationBreadCrumb();
MediaReceiver.Dispose();
MediaSession.Dispose();
MyLogger.Trace("MyMediaPlayer: CloseEntireApp is Killing App. Good bye!");
service.StopSelf();
Android.OS.Process.KillProcess(Android.OS.Process.MyPid());
}
Here is the OnCreate method for my service:
public class MyMediaPlayer : MediaBrowserServiceCompat
{
private static MediaPlayer? player;
private static MusicAppFile? currentFile;
private static List<MusicAppFile>? allFilesInCurrentContext;
private static Context? context;
private static List<int> recentIndexes = new List<int>();
private static int maxRecentIndexes = 30;
private static bool shuffleMode = false;
private static ViewGroup? Parent;
private static NotificationManager? manager;
private static CancellationTokenSource? cts;
public static MediaButtonReceiver? MediaReceiver;
public static MediaSessionCompat? MediaSession;
private static PlaybackStateCompat.Builder stateBuilder;
private static MediaBrowserServiceCompat service;
public IBinder Binder { get; private set; }
public const string ActionPlay = "com.xamarin.action.PLAY";
public const string ActionPause = "com.xamarin.action.PAUSE";
public const string ActionNext = "com.xamarin.action.NEXT";
public const string ActionStop = "com.xamarin.action.STOP";
public const string ActionBack = "com.xamarin.action.BACK";
public const string ActionCloseApp = "com.xamarin.action.CLOSEAPP";
public static string ChannelId = "NowPlayingNote";
public static string MY_MEDIA_ROOT_ID = "media_root_id";
public static int MY_MEDIA_NOTIFICATION_ID = 1111111;
public static string MY_MEDIA_TAG = "media_tag";
public override void OnCreate()
{
base.OnCreate();
// Create a MediaSessionCompat
MediaSession = new MediaSessionCompat(context, MY_MEDIA_TAG);
// Enable callbacks from MediaButtons and TransportControls
MediaSession.SetFlags(
MediaSessionCompat.FlagHandlesMediaButtons |
MediaSessionCompat.FlagHandlesTransportControls);
// Set an initial PlaybackState with ACTION_PLAY, so media buttons can start the player
stateBuilder = new PlaybackStateCompat.Builder()
.SetActions(
PlaybackStateCompat.ActionPlay |
PlaybackStateCompat.ActionPlayPause |
PlaybackStateCompat.ActionSkipToNext |
PlaybackStateCompat.ActionSkipToPrevious |
PlaybackStateCompat.ActionStop);
MediaSession.SetPlaybackState(stateBuilder.Build());
// MySessionCallback() don't do this. C# isn't as good at doing callbacks because you can't define them inline
// MediaSession.SetCallback(new MediaSessionCallback(this));
service = this;
// Set the session's token so that client activities can communicate with it.
SessionToken = MediaSession.SessionToken;
}
...
I create this service when they click to select a file in one of the menu activities (so in a method called by a method called by an OnClick delegate):
if (musicMenu != null)
{
bool stillPlayingSameFile = MyMediaPlayer.UpdateCurrentContext(c, musicMenu, mf);
if (cts == null)
{
// Start the service and tell it to call play
InitiateMediaBrowserService(c);
} else
{
MyMediaPlayer.Play(stillPlayingSameFile);
}
}
GoToNowPlaying(c, mf);
and the inner service there:
public static void InitiateMediaBrowserService(Context c)
{
// Start the service and tell it to call play
Intent intent = new Intent(c, typeof(MyMediaPlayer));
intent.SetAction(MyMediaPlayer.ActionPlay);
cts = new CancellationTokenSource();
Platform.AppContext.StartForegroundService(intent);
}
Ok, so now the play service, which is triggered from the action play intent here, and makes the call to start the notification, which is where the StartForeground call is made (see the first snippet at the top):
public static void Play(bool stillPlayingSameFile)
{
// If the player has not been created before, or it is a new track, then it needs to be recreated
if (player == null || !stillPlayingSameFile)
{
// If we're here to recreate the player, destroy the old one in memory first
if (player != null)
player.Release();
// Then add the new player
if (currentFile != null)
{
Uri uri = Android.Net.Uri.Parse(currentFile.FilePath);
MediaPlayer media = MediaPlayer.Create(context, uri);
media.Completion += OnCompletion;
if (MediaReceiver == null)
MediaReceiver = new MediaButtonReceiver(context);
media.RoutingChanged += MediaReceiver.OnRoutingChanged;
player = media;
player.SetWakeMode(context, WakeLockFlags.Partial);
}
// Finally, add this file to the list of those recently played
int indexToPlay = allFilesInCurrentContext.IndexOf(currentFile);
if (indexToPlay >= 0)
recentIndexes.Add(indexToPlay);
if (recentIndexes.Count > maxRecentIndexes)
recentIndexes.RemoveAt(0);
}
// Finally start the player, which picks up where left off if this is the same track
if (!IsPlaying() || !stillPlayingSameFile)
{
player.Start();
NotificationNowPlayingBreadCrumb();
}
}
The MediaButtonReceiver and MediaBroadcastReceiver classes are pretty straightforward, so comment if you really need that code. One other thing to note is that you do have to bind the service to an activity (I suggest the now playing activity):
protected override void OnStart()
{
base.OnStart();
//Config.ConfigureBluetoothIntegration(this); TODO remove this
Intent serviceToStart = new Intent(this, typeof(MyMediaPlayer));
//serviceToStart.SetAction(MyMediaPlayer.ActionPlay);
BindService(serviceToStart, new ServiceConnection(this), Bind.AutoCreate);
}
So there, now there IS an example of how to use the MediaSession and MediaSessionCompat and MediaBrowserServiceCompat online somewhere. Even ChatGPT could not find an example or tell me how to do this. You are welcome, internet. Enjoy your coding!
I have sensor like this: https://easyelectronyx.com/wp-content/uploads/2017/03/flame.jpg?i=1
Please can anyone help me ? I need to find code to read from it , in C#. I have Raspberry Pi 2 Model B , Windows 10 IoT Core and programming in C#. I cant find documentation on the Internet. Is it needed to wire Analog output ?
Thanks
This Frame sensor device can provide digital or analog output based on its datasheet.
If you don't like to use analog output you can get output from digital pin DO.
First, connect the Frame sensor and Raspberry Pi. Connect VCC, GND and DO like the following picture show. For digital pin, I choose GPIO27 here, you can choose other pin you like.
Second, write the code. Create the UWP app(Start here).
MainPage.xaml
<StackPanel VerticalAlignment="Center" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Name="SensorOuputValue" />
</StackPanel>
MainPage.xaml.cs
public sealed partial class MainPage : Page
{
private const int SENSOR_PIN = 27;
private GpioPin pin;
private GpioPinValue pinValue;
private DispatcherTimer timer;
public MainPage()
{
InitializeComponent();
timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(1000);
timer.Tick += ReadSensor;
InitGPIO();
if (pin != null)
{
timer.Start();
}
}
private void InitGPIO()
{
var gpio = GpioController.GetDefault();
// Show an error if there is no GPIO controller
if (gpio == null)
{
pin = null;
System.Diagnostics.Debug.WriteLine("There is no GPIO controller on this device.");
return;
}
pin = gpio.OpenPin(SENSOR_PIN);
pin.SetDriveMode(GpioPinDriveMode.Input);
System.Diagnostics.Debug.WriteLine("GPIO pin initialized correctly.");
}
private void ReadSensor(object sender, object e)
{
SensorOuputValue.Text = pin.Read().ToString();
}
}
All these comes from the idea that i want to use the SerialPort class in .Net , but the only way is by calling dll . Because i can only get interfaces from the program calling this dll. mycode is below.
i wrote a class about serialport,
public class CommClass
{
public SerialPort _port;
private string _receivedText;
public string receivedText
{
get { return _receivedText; }
set
{
_receivedText = value;
}
}
public CommClass(string _pname)
{
portList = SerialPort.GetPortNames();
_port = new SerialPort(portList[0]);
if (portList.Length < 1)
_port= null;
else
{
if(portList.Contains(_pname.ToUpper()))
{
_port = new SerialPort(_pname);
_port.DataReceived += new SerialDataReceivedEventHandler(com_DataReceived);
}
}
}
private void com_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
string indata = _port.ReadExisting();
receivedText = indata;
}
}
from Bytestoread i can see there r data coming in and i can get data from port.ReadExisting(), but receivedText did not change ,it did not hit the SerialDataReceived event . Is my way wrong?any suggestion?thanks
i created a dll from CommClass ,then i call it in my winform program which has a button and a textbox . Clicking the button , then i initialize the port
public Form1()
{
InitializeComponent();
}
public CommClass mycom;
private void button1_Click(object sender, EventArgs e)
{
mycom = new CommClass("com3");
mycom._port.Open();
textbox.Text=mycom.receivedText;//i add a breakpoint at this line ,
}
when hitting it , i check mycom._port.PortName is "com3", its IsOpen() is "Open" , i use virtual port to send data . i send "1111",then check the mycom._port.BytestoRead is 4, and mycom._port.ReadExisting() is "1111", but mycom.receivedText is null. My puzzle is that i have no idea when the data is coming . How to use the DataReceived event in my winform without code "using System.Io.Ports",just with reference CommClass.dll. Did i make it clear? Thanks for help.
mycom._port.Open();
textbox.Text=mycom.receivedText;//i add a breakpoint at this line ,
That code cannot work, it is a threading race bug. The DataReceived event does not fire instantly after you open the port. It will take a microsecond or so, give or take. A threadpool thread has to get started to fire the event. And of course the device actually has to send something, they usually only do so when you transmit something first.
Which clearly did not happen, your DataReceived event handler has a bug as well. It is not allowed to update the Text property of a control in that event since it runs on a worker thread. Your program will bomb with an InvalidOperationException.
You'll have to write something like this instead:
private void com_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
string indata = _port.ReadExisting();
this.BeginInvoke(new Action(() => {
textbox.AppendText(indata);
}));
}
With the additional stipulation that you must not leave it this way, updating the Text property of a TextBox and making it visible on the screen is an expensive operation that's going to turn your user interface catatonic when the device starts transmitting data at a high rate.
I want bind the master sound volume of windows to a slider in my program. So I searched and found some ways to GET or SET master volume + some libraries like these:
Change master audio volume from XP to Windows 8 in C#
Handling volume control in C#
NAudio
Some where I see a code with LOOP to get volume value: loop
Some where I see a code with TIMER to get volume value...
Also I see some samples to determine volume but after test one of them I saw some errors at runtime in Windows 8: C# – Adjust master volume in Vista and Windows 7
EDIT:
Now i have the following class. i create an instance of it and use propertchange event to show volume by Trace.WriteLine. but when i change the windows volume it cause an unhandeled error!
public class AudioEndpointVolumeEnforcer : INotifyPropertyChanged
{
private MMDeviceEnumerator mmDeviceEnumerator;
private MMDevice mmDevice;
private AudioEndpointVolume audioEndpointVolume;
private bool _deviceIsMuted;
private int _desiredVolume;
private int _volumePercent;
public AudioEndpointVolumeEnforcer()
{
try
{
mmDeviceEnumerator = new MMDeviceEnumerator();
mmDevice = mmDeviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia);
audioEndpointVolume = mmDevice.AudioEndpointVolume;
audioEndpointVolume.OnVolumeNotification += data =>
{
VolumePercent = Convert.ToInt16(data.MasterVolume*100);
_deviceIsMuted = data.Muted;
};
DesiredVolume = 65;
}
catch (Exception ex)
{
// Logging logic here
}
}
public int DesiredVolume
{
get { return _desiredVolume; }
private set
{
if (_desiredVolume == value) return;
_desiredVolume = value;
//NotifyOfPropertyChange();
OnPropertyChanged("DesiredVolume");
Enforce(_desiredVolume);
}
}
public int VolumePercent
{
get { return _volumePercent; }
private set
{
if (_volumePercent == value) return;
_volumePercent = value;
if (_volumePercent != _desiredVolume)
{
_volumePercent = _desiredVolume;
Enforce(_volumePercent);
}
}
}
public void Enforce(int pct, bool mute = false)
{
var adjusted = Convert.ToInt16(audioEndpointVolume.MasterVolumeLevelScalar*100);
if (adjusted != DesiredVolume)
{
audioEndpointVolume.MasterVolumeLevelScalar = pct/100f;
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Use the class:
// Inside my window cunstractor >>
audioVolume = new AudioEndpointVolumeEnforcer();
audioVolume.PropertyChanged += MasterAudioVolumeChanged;
private void MasterAudioVolumeChanged(object obj, PropertyChangedEventArgs eventArgs)
{
Trace.WriteLine(eventArgs.PropertyName+" - "+audioVolume.DesiredVolume);
}
Runtime Error:
......................................
The Output panel show Access violation error:
The program '[18488] Audio.exe' has exited with code -1073741819 (0xc0000005) 'Access violation'
Edit
I tested the above code by breakpoints and trace. the above error happens sometimes in the bellow part:
audioEndpointVolume.OnVolumeNotification += data =>
{
VolumePercent = Convert.ToInt16(data.MasterVolume*100);
_deviceIsMuted = data.Muted;
};
For example sometimes it happens in this line:
_deviceIsMuted = data.Muted;
But when i go to the next step by F11 it dose not show a normal error inside the program! It cause the bellow error window and application force closing!
......................................
Access violation
You can use the NAudio library as such:
using NAudio;
using NAudio.CoreAudioApi;
private static MMDeviceEnumerator enumer = new MMDeviceEnumerator();
private MMDevice dev = enumer.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
public void Form1_Load(object sender, EventArgs e){
dev.AudioEndpointVolume.OnVolumeNotification += AudioEndpointVolume_OnVolumeNotification;
}
void AudioEndpointVolume_OnVolumeNotification(AudioVolumeNotificationData data)
{
// This shows data.MasterVolume, you can do whatever you want here
MessageBox.Show(data.MasterVolume.ToString());
}
This example uses a WinForms project, but you could also create the event elsewhere.
NAudio can be installed as a NuGet package, or from https://naudio.codeplex.com/
The volume ranges from 0.0f to 1.0f, to get 0-100 simply do:
(int)(data.MasterVolume * 100f)
Does your code update a control in response to the event, say a trackbar or a mute button? If so, you may need to use a thread-safe approach. Controls can only be updated by the UI thread. Me.InvokeRequired checks to see if it's the UI thread that wants to do the update. If not, it returns True. Me.Invoke then uses a delegate to handle communication between the two threads. If you don't use Me.Invoke, there will be an access violation.
Delegate Sub UpdateCallback(Volume As Integer, Muted As Boolean)
Public Overloads Sub Update(Volume As Integer, Muted As Boolean)
If tbVolume.InvokeRequired Then
Dim d As New UpdateCallback(AddressOf Update)
Me.Invoke(d, Volume, Muted)
Else
tbVolume.Value = Volume
_Mute = Muted
btnMuteUnmute.BackgroundImage = DirectCast(If(_Mute, _
My.Resources.mute, My.Resources.Unmute), Icon).ToBitmap
End If
End Sub
I have a Canvas control with various elements on, in this particular function I am allowing a user to drag the end point of a line around the canvas. In the MouseMove function I call e.GetPosition().
The function is, according to the VS performance analyzer, close to 30% of total CPU for the app when constantly moving around. Its pretty slow. What can I do to increase this performance?
CurrentPoint = e.GetPosition(PointsCanvas);
I've faced the same problem while using MouseMove on windows phone 8. It seems that while dragging , events (containing the coordinates you need ) are raised at regular time interval ( depending on what you do in the implementation in your listeners, every 20 ms for example). So what I did was to populate a Queue with my coordinates and create a Thread that consume that Queue by enqueue the first element and do the logic I want. Like that the logic is not done serially because it's another thread who does the job.
I don't know if I'm enough clear so please take a look to the code below :
//Class used to store e.getPosition(UIElement).X/Y
public class mouseInformation
{
public int x { get; set; }
public int y { get; set; }
public mouseInformation(int x, int y, String functionName)
{
this.x = x;
this.y = y;
}
}
private readonly Queue<mouseInformation> queueOfEvent = new Queue<mouseInformation>();
//MouseMove listener
private void wpCanvas_MouseDragged(object sender, System.Windows.Input.MouseEventArgs e)
{
//Instead of "wpCanvas" put the name of your UIElement (here your canvas name)
mouseInformation mouseDragged = new mouseInformation((int)e.GetPosition(wpCanvas).X, (int)e.GetPosition(wpCanvas).Y);
EnqueueMouseEvent(mouseDragged);
}
//Allow you to add a MouseInformation object in your Queue
public void EnqueueMouseEvent(mouseInformation mi)
{
lock (queueOfEvent)
{
queueOfEvent.Enqueue(mi);
Monitor.PulseAll(queueOfEvent);
}
}
//Logic that your consumer thread will do
void Consume()
{
while (true)
{
mouseInformation MI;
lock (queueOfEvent)
{
while (queueOfEvent.Count == 0) Monitor.Wait(queueOfEvent);
MI = queueOfEvent.Dequeue();
}
// DO YOUR LOGIC HERE
// i.e DoSomething(MI.x, MI.y)
}
}
And don't forget to create the thread in your Main() or in MainPage_Loaded(object sender, RoutedEventArgs e) method if you are Windows phone user.
System.Threading.ThreadStart WatchQueue = new System.Threading.ThreadStart(Consume);
System.Threading.Thread RunWatchQueue = new System.Threading.Thread(WatchQueue);
RunWatchQueue.Name = "Events thread";
RunWatchQueue.Start();
To be simple less you do in your MouseMove listener, more speed it will be.
You can aswell do the logic asynchronously or even use Bresenham algorithm to simulate more events.
Hope it helps.
Are you using any effects such as dropshaddow etc?
I recently had the situation where e.GetPosition() was also using 30% of the app's cpu resources, which doesn't make any sense right?
I turns out that up the visual tree there was a control applying a dropshaddow effect and that was what was slowing everything down so much...