How to grey out widgets on Arduino IoT cloud? - arduino

Good Day,
I'm using Arduino IoT Cloud in a personal project.
Within the project there are two modes, "auto" and "manual". When the auto mode is selected, I need a switch widget to be inactive(greyed out) so the user cannot tap on it.
I have found a workaround in the interim, i.e. when the user is in auto mode and taps the switch, it will change its state on the widget, but then I reverse its state to initial before the tap.
eg. If you're in auto mode and the switch is on, and is then pressed (i.e. goes from on to off) then I change the switch widget state back to on. Below is the workaround.
void onSwitchStateChange() {
if (modeState == true) { // when the mode is auto
if (switchState == true) { // if switch state changes to "On" by tapping on the widget
switchState = false; // set the switch state back to its original state before the tap
}
else if (switchState == false) { // if switch state changes to "Off" by tapping on the widget
switchState = true; // set the switch state back to its original state before the tap
}
}
}
Is there a way "grey-out" the switch ?

Related

Arduino; How to stop listening to button press in loop while in specific function?

For a project I'm trying to get out of the Arduino loop() function and 'loop' in another function. I'm trying to make a sort of menu where the user can loop through and press an OK-button to confirm.
My setup is as follows;
Up button, OK button, Down button, set temperature button
Whenever the user pushes the temperature button, I'd like to give the user 2 options (using a LCD); change the minimum or the maximum temperature. I'd like to cycle through these options using the up and down button and give the user the option to confirm either of these temperature changes with the OK button. For some reason the Arduino doesn't listen to any button presses after I press the temperature button anymore. See code example below.
const int UpButton = 3;
const int OKButton = 4;
const int DownButton = 5;
const int ChangeTemperatureButton = 6;
void setup() {
pinMode(UpButton, INPUT_PULLUP);
pinMode(OKButton, INPUT_PULLUP);
pinMode(DownButton, INPUT_PULLUP);
pinMode(ChangeTemperatureButton, INPUT_PULLUP);
}
void loop() {
if (!digitalRead(ChangeTemperatureButton)) {
changeTemperature();
}
if (!digitalRead(OKButton)) {
// set LCD to 'not good'
}
}
void changeTemperature() {
// set LCD to 'Changing temperature'
if (!digitalRead(OKButton)) {
// set LCD to 'Set max temperature'
}
}
Whenever I press the button to change the temperature it does set the LCD to 'Changing temperature'. After that I press the OK button and instead of it changing to 'Set max temperature' it changes to 'not good', which is in the loop. I think I fundamentally understand something wrong here, can anyone help me out with this one?
TL;DR: I'm trying to get and stay out of the loop when I press a certain button so my button can do something else than is defined in the loop. After everything in the changeTemperature function is set and done, I'd like to return to the loop function.
When you press the ChangeTemperatureButton, and your program enters the changeTemperature() function, it immediately sets the LCD to 'Changing temperature' as you said.
The next thing it does is evaluate the condition of the if statement.
if (!digitalRead(OKButton)) {
If the condition of an if statement is false, then it is skipped over.
So if you are not simultaneously holding down the OKButton when you press the ChangeTemperatureButton, then that code will be skipped, and hence you are not seeing the 'Set max temperature' on your LCD.
I think I see though what you after, which is basically having a sub-menu, which is a very common feature of menus in general.
You might consider looking at some example code for LCD menus that use submenus to get an idea of how to implement your code.
You've gotten a good suggestion from YouJeng. To expand on that answer...
In your loop you'll do your initial button detection and debouncing work. Once the user presses the ChangeTemperatureButton your loop should call another function say "changeTemp()". You'll keep yourself in this function until you hit "OK".
There are tons of ways to accomplish this, but here's some simple pseudo-code:
void loop() {
//Draw your main menu buttons
//Handle button presses
if(ChangeTemperatureButton) {
changeTemp()
}
//Anything else your sketch needs to do
}
void changeTemp() {
bool okPressed = false;
//Display your text for the buttons in this mode
//Increase, decrease, OK (for example)
if(increase) {
//do something
}
if(decrease) {
//do something
}
if(OK) {
okPressed = true;
}
if(!okPressed) { //This essentially keeps you in this mode until you hit the OK button
changeTemp();
}
}
There are certainly cleaner ways to do this, but this is exactly how I've done it on other projects and it works great for a quick prototype.

DispatchKeyEvent stops firing after Xamarin Forms Entry control IsFocused

I am building a Xamarin Forms mobile app that runs in Android on a Zebra scanner. I flip 2 different StackLayouts to IsVisble true/false to display different stuff in the UI. (StackLayout1 and StackLayout2)
The customer wants the user to be able to use the app entirely from the hardware keyboard on the scanner. So I have used the device Settings so that it never displays the virtual keyboard (I don’t think that matters for the issue I am having.)
I am overriding DispatchKeyEvent in a PageRenderer in the Android project and everything is working great … except.
The problem case:
StackLayout1 is displayed
the user taps an Entry control, putting the focus there
the user taps a button in the UI
the app displays StackLayout2
at this point the DispatchKeyEvent never fires no matter what key I press on the device keyboard
If an Entry box does NOT get the focus (step #2 above) the DispatchKeyEvent always fires in StackLayout2 and the StackLayouts display as expected.
If I programatically put the focus in an Entry box in StackLayout2 at step #3 above the DispatchKeyEvent fires fine.
That is not an OK solution. I have tried to progamatically put the focus on StackLayout2, and that code seems to do what is expected but DispatchKeyEvent does not fire.
Maybe I need to do something in the Android-project PageRenderer so that it is aware of StackLayout2 when it is made IsVisible = true.
Update 2: I found that I did NOT need custom StackLayouts. The solution which I posted below does not include any of this stuff I am describing in Update 1 (sorry, if that's confusing).
Update 1:
I added a ViewRenderer for both StackLayouts, and the code is hitting the OnElementChanged event when StackLayout2's IsVisible property flips to true, just great. Although the problem case is the same: DispatchKeyEvent does not fire once StackLayout2 is displayed, if an EntryBox had the focus in StackLayout1
Here is the OnElementChanged part of the new StackLayout ViewRenders
async void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "IsVisible":
if (Element.IsVisible)
{
if (sender is StackLayout)
{
this.FocusableViewAvailable(this); // if I comment these 2 lines out I get the same bad result
this.Focusable = true; // if I comment these 2 lines out I get the same bad result
this.FocusableInTouchMode = true;
var dd = this.RequestFocus(); // this is always false
var ee = this.IsFocused; // this is always false
}
}
break;
}
}
Also, as I am pointing out in the comments ^ there, IsFocused is always false.
Ideas?
My hunch, "Maybe I need to do something in the Android project PageRenderer" was correct. In the DispatchKeyEvent I had to make the MainPage have the focus when the keypress was handled.
Here is what the DispatchKeyEvent looks like now (notice the comments):
public override bool DispatchKeyEvent(KeyEvent ke)
{
// MainPage.ReceiveKeyPress(e); is the method that this method returns to
bool KeyPressWasHandled = false;
KeyPressWasHandled = (Element as MainPage).ReceiveKeyPress(ke);
if (KeyPressWasHandled)
{
// this next block seems to be needed so that this class
// continues to receive the keypress event after an Entry box has had the focus
this.Focusable = true;
this.FocusableInTouchMode = true;
this.RequestFocus();
return true; // returning true tells the parent class that the keypress has been handled
} else
{
try
{
return base.DispatchKeyEvent(ke);
}
Now the "problem case" in my initial post is no longer a problem.
NOTE: I found that I did NOT need the custom ViewRenderers that I had made for the StackLayouts.

Xamarin Forms - Duplicate Screens

I'm writing an app using Xamarin Forms and I have an issue I was hoping someone can help with.
My app contains a screen which has multiple icons that can be pressed which would then open a new screen.
My issue is that if you press the icon twice really fast, the app opens up 2 instances of the same screen (it's not just related to a double press, if you press the icon 6 times very fast it will open up 6 duplicate screens). Pressing the Back button, closes the top screen to reveal the duplicate screen underneath. Pressing the Back button again navigates you back to the original screen.
This issue seems to occur on any screen within my app so I'm hoping other people will have experienced it and know of a solution to prevent duplicate screens being displayed.
This is a known issue in TapEvents.
My hack is, in the code-behind, have a bool variable _canTap.
Inside the method you are calling to push new page, first you check if canTap, then set to false, and only set to true after navigating to another page. This way all taps will be disregarded.
Example:
private bool _canTap = true;
public void YourMethod()
{
if(_canTap)
{
_canTap = false;
YourMethodToNavigate();
_canTap = true;
}
}
In the Icon_Pressed method add this,
this.IsEnabled = false;
await Navigation.PushAsync(new MyPage());
this.IsEnabled = true;
It disables the page until the current Icon pressed event is finished
This is known problem with Xamarin apps. I've used a private variable combined with a try-finally pattern to solve this. Ex:
bool allowTap = true;
public void ButtonTapped()
{
try
{
if(allowTap)
{
allowTap = false;
// Do whatever...
}
}
finally
{
allowTap = true;
}
}
The finally makes sure allowTap gets set back to true no matter what happens, short of a complete crash. Note that you can also use a catch block between the try and finally blocks to grab any errors if needed.

Dragging a radio button performes the action but doesn't set the check

I am creating an application that should work on desktop and some mobile platforms.
The following example creates and connects my portrait/landscape buttons, in a group, to a slot, on the release signal.
m_landscapeRadio = new QRadioButton(QObject::tr("Landscape "));
m_portraitRadio = new QRadioButton(QObject::tr("Portrait "));
m_orientationGroup.addButton(m_landscapeRadio, 0);
m_orientationGroup.addButton(m_portraitRadio, 1);
m_orientationGroup.setExclusive(true);
m_landscapeRadio->setChecked(true);
connect(&m_orientationGroup, SIGNAL(buttonReleased(int)), this, SLOT(orientationSlot(int)));
But I found a weird situation:
Assume landscape button is checked. If I press and drag away from the portrait radio button, the slot action is performed (for the portrait option) but the portrait button is not checked.
I would like the action not to be performed.
For now...
In the orientationSlot I test the argument and set the checked value myself... Though I really expected the buttons to know to do that themselves.
But I think it is more expected by users that, if the press a button and change their mind, to be able to drag away from the button and not have the action be performed.
I can handle verifying if the check really happened in the action slot, and either check or discard the action depending on how I will think the user experience is better...
If I want the buttons to be checked and to perform the action as well:
void MyWidget::orientationSlot(int checked)
{
if(checked) m_portraitRadio->setChecked(true);
else m_landscapeRadio->setChecked(true);
.... actual actions
}
If I want the action not to be performed when the user drags away from the button (my preferred option):
void MyWidget::orientationSlot(int checked)
{
if(m_orientationGroup.checkedId() != checked) return;
.... actual actions
}
I use QRadioButton and handle mouse button being released event for reacting
on radio button being switched. It causes problems altogether with dragging event. I would like to either get the button
to be checked, or the action not to be performed.
http://doc.qt.io/qt-5/qradiobutton.html
Whenever a button is switched on or off, it emits the toggled()
signal. Connect to this signal if you want to trigger an action each
time the button changes state. Use isChecked() to see if a particular
button is selected.
Either you connect the radio button to the handler explicitly or the whole group: http://doc.qt.io/qt-5/qbuttongroup.html#buttonToggled
void QButtonGroup::buttonToggled(QAbstractButton *button, bool
checked)
This signal is emitted when the given button is toggled. checked is
true if the button is checked, or false if the button is unchecked.
Note: Signal buttonToggled is overloaded in this class. To connect to
this one using the function pointer syntax, you must specify the
signal type in a static cast, as shown in this example:
connect(buttonGroup, static_cast<void(QButtonGroup::*)
(QAbstractButton *, bool)>(&QButtonGroup::buttonToggled),
[=](QAbstractButton *button, bool checked) {
if (button == m_portraitRadio) {
// Portrait (un)checked
if (checked)
{
// checked!
}
}
/* ... */ });

How do you make an image stay on screen in Processing with an Arduino switch?

I am connecting Processing and an Arduino pushbutton. It's successfully connected with Standard Firmata. What I want to happen is that when the button is pressed, an image will show up in Processing but also stay on the screen, just like the LED, and then when the button is pressed again, the image will disappear from the screen. I'm just testing it with shapes for now. I have tested it with an LED and that works fine. Any ideas what I'm doing wrong? This is the code I have:
void draw()
{
buttonState = arduino.digitalRead(buttonPin);
if (buttonState == arduino.HIGH && buttonPressed == 0)
{
buttonPressed = 1;
rect(10, 10, 10, 10);
text("hello", 10, 10);
}
if (buttonState == arduino.LOW && buttonPressed == 1)
{
buttonPressed = 0;
rect(50, 50, 10, 10);
}
}
I'm not sure how you've wired up your button. I'm assuming it goes HIGH when you press it.
Currently, your first if statement will get triggered when the button is pressed, but only if it wasn't pressed last time you checked (i.e. it's effectively rising edge triggered).
Your second if statement will get triggered when the button is released, but only if it wasn't released last time you checked (i.e. it's effectively falling edge triggered).
If you want the button to toggle something each time it's pressed, then you will probably need to put most of your logic into the first if statement (except the buttonPressed stuff). You will need to store some kind of value which says if the image is currently visible. If it's visible when the button is pressed, then hide it (and vice versa).
For example:
boolean imageVisible = false;
void draw()
{
buttonState = arduino.digitalRead(buttonPin);
if (buttonState == arduino.HIGH && buttonPressed == 0)
{
buttonPressed = 1;
if (imageVisible) {
// Hide image here...
} else {
// Show image here...
}
imageVisible = !imageVisible;
}
if (buttonState == arduino.LOW)
{
buttonPressed = 0;
}
}
Note: I removed the buttonPressed check from the second if statement. It's only necessary if you actually need to respond to a falling-edge event. For a simple toggle, it's not important.

Resources