Push button ( physical and virtual )

Hi all.
This is scenario. I have a push button to activate a door open. It’s need a 1,5 second push to activate the motor.
So to do this from Arduino with Cayenne I have write this code:

CAYENNE_IN(VIRTUAL_PIN5)
{
//get value sent from dashboard
int currentValue = getValue.asInt(); // 0 to 1

// assuming you wire your relay as normally open
if (currentValue == 0) {
digitalWrite(RELAY_DIGITAL_PIN5, HIGH);
delay(1500);
digitalWrite(RELAY_DIGITAL_PIN5, LOW);
}
else {
digitalWrite(RELAY_DIGITAL_PIN5, HIGH);
delay(1500);
digitalWrite(RELAY_DIGITAL_PIN5, LOW);
}
}

So the relay go HIGH for 1500ms and the return LOW to open. And in the same way to close. The code works without problems. One click on dashboard and door open. Another click and door close.

I have also a control of state of the door ( if open or close ) on Pin D9.
And I have a “physical” push button to perform the same operation.

The problem is for example if I open from physical button , how i can put the button icon of dashboard to on.

It’s possible to modify the state of virtual button in dashboard , without being called the code above.

I’ll explain.
I push the “physical” button, so I have on D9 the value HIGH ( the door is open ).
If i write :

CAYENNE_OUT(VIRTUAL_PIN5)
{
bool stato=digitalRead (DIGITAL_INPUT9);
if (stato == HIGH) {
Cayenne.virtualWrite (VIRTUAL_PIN5, 1);
}
else{
Cayenne.virtualWrite (VIRTUAL_PIN5, 0);
}
}

It’ correct. Or this code go to recall the function CAYENNE_IN ??

Pin D5 is a relay
Pin V5 is the virtual pin of button in dashboard that activate relay
Pin D9 is a control from door (HIGH → open or LOW → close )

Any ideas ?

Thanks

I tried but it does not work.

I can read the status of relay ( digitalRead(9) ) but i can’t change the status of virtual button in Cayenne dashboard.

any suggestion ?

getValue.asInt() return the state of button ( 0 or 1 ) ,Ii think that I need to change this value.

Thank a lot.

Hi @fagallo79,

Are you sure it is not actually working and you are actually triggering another sequence by changing the state of V5?
Can you try a button on V5 and an value indicator on V6 and see what happens with the following code:

Note, the timner libary pulse function is recommended as it does not require you to sit for 1500ms in the CAYENNE_IN function. You need to install it from github. Link in code.

#include <Timer.h> //https://github.com/JChristensen/Timer

Timer t;

void setup()
{
  Serial.begin(9600);
  Cayenne.begin(token, ssid, password);
}

void loop()
{
  Cayenne.run();
  t.update();

}

//pulses digital pin 5 on change of virtual pin 5
CAYENNE_IN(VIRTUAL_PIN5)
{
  t.pulse(RELAY_DIGITAL_PIN5, 1500, 1); //pin, time(milliseconds), initial state
}

//setup dashboard indicator on channel 6
CAYENNE_OUT(VIRTUAL_PIN6)
{
    Cayenne.virtualWrite(VIRTUAL_PIN6, digitalRead(DIGITAL_INPUT9));
}

Cheers,

Craig

1 Like

Hi Craig, thanks for the advice of Timer.h library.
I tried the sketch. I did two tests.

In the first test, I created in dashboard a “New device” → “Custom Wigdget” → “Value”; In the second a “Custom Wigdget” → “Button”.

I associated both to VIRTUAL_PIN7 (obviously first one and then the other)

// Setup dashboard indicator on channel 7 ( the six is used )
CAYENNE_OUT (VIRTUAL_PIN7)
{
Cayenne.virtualWrite (VIRTUAL_PIN7, digitalRead (DIGITAL_INPUT9));
}
</ Code>

If is a “Value widget”, this turns on (green) when the door is open (DIGITAL_INPUT9 == HIGH) and turns off when it closed.

If is a “Button widget” nothing happens.

The question is this: how can you change the status of the “button” in dashboard from an external souce (physical button) ??

If the door is open, the button must be switched on (eg. Illuminated key symbol). And then the pressure on the dash board must make the closing of the door.

I hope I explained myself and sorry for my bad English

@fagallo79,

I think the custom button may be broken.

I have been using this functionality in my BBQ project so I know it works.

Try this:

Add a new generic actuator
Set it to Virtual
Set your channel to 7
Select Button

Unfortunately, setting the channel is broken in the browser, but you can use the mobile app. Here’s a screen shot.

My BBQ code:

//#define CAYENNE_DEBUG         // Uncomment to show debug messages
#define CAYENNE_PRINT Serial  // Comment this out to disable prints and save space
#include <WiFi101.h>
#include <CayenneMKR1000.h>
#include <Timer.h>
#include <PID_v1.h>
#include <SPI.h>
#include <MAX31855_SPI.h>

//Define Variables we'll be connecting to
double Setpoint, Input, Output,PID_Input;

#define AugerPin 2

//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint,0.5,1,5, DIRECT);

int WindowSize = 30000;
unsigned long windowStartTime;
byte cs = 0; //chip select is DIO 0
double internal_temp;
double coupler_temp;
MAX31855_SPI *max31855;

int running = 0;

Timer t;

// Cayenne authentication token. This should be obtained from the Cayenne Dashboard.
char token[] = "";
//char token[] = "";
// Your network name and password.
char ssid[] = "";
char password[] = "";

void setup()
{
  max31855 = new MAX31855_SPI(cs);
  SPI.begin();

  //relays
  pinMode(1,OUTPUT);
  pinMode(2,OUTPUT);
  pinMode(3,OUTPUT);
  pinMode(4,OUTPUT);
  
  //setup the pid
  windowStartTime = millis();

  //initialize the variables we're linked to
  Setpoint = 100;

  //tell the PID to range between 0 and the full window size
  myPID.SetOutputLimits(0, WindowSize);

  //turn the PID on
  myPID.SetMode(AUTOMATIC);
  
  Serial.begin(9600);
  Cayenne.begin(token, ssid, password);
}

void loop()
{
  internal_temp = max31855->readInternal();
  internal_temp = internal_temp * 9 / 5 + 32;
  coupler_temp = max31855->read();
  coupler_temp = coupler_temp * 9 / 5 + 32;
  
  Cayenne.run();
  
  t.update();

  if(running)
  {
    Input = coupler_temp;
    myPID.Compute();
  
    /************************************************
     * turn the output pin on/off based on pid output
     ************************************************/
    unsigned long now = millis();
    if(now - windowStartTime>WindowSize)
    { //time to shift the Relay Window
      windowStartTime += WindowSize;
    }
    if(Output > now - windowStartTime) digitalWrite(AugerPin,1);
    else digitalWrite(AugerPin,0);    
  } 
  else digitalWrite(AugerPin,0);    
}

//setpoint
CAYENNE_IN(V0)
{
  Setpoint = getValue.asInt()/1000;

  Serial.print("Setpoint:");
  Serial.println(Setpoint);
}

//cold junction temp
CAYENNE_OUT(V1)
{
  Cayenne.virtualWrite(V1,internal_temp);
}

//thermocouple temp
CAYENNE_OUT(V2)
{
  Cayenne.virtualWrite(V2,coupler_temp);
}

//start button
CAYENNE_IN(V3)
{
  if (getValue.asInt())
  {
    running = 1;    

    //indicate running
    Cayenne.virtualWrite(V8,1);

    //clear start button
    Cayenne.virtualWrite(V3,0);

    //start fan
    digitalWrite(3,1);

    //indicate fan running
    Cayenne.virtualWrite(V7,1);
    
    //start ignitor cycle
    ignite();           
  }
}

//shutdown button
CAYENNE_IN(V4)
{
  if (getValue.asInt())
  {
    running = 0;    

    //indicate shutting down
    Cayenne.virtualWrite(V8,0);
    Cayenne.virtualWrite(V9,1);

    //do some stuff
    
    t.after(5000,shutdown);

    //clear shutdown button
    Cayenne.virtualWrite(V4,0);
  
  }
}

//hopper low
CAYENNE_OUT(V6)
{
  Cayenne.virtualWrite(V6,digitalRead(2));
}

void shutdown()
{
  //check temp is cool
  //send error messages, etc.
  //turn off fan
  //clear internal variables

  //ignitor
  Cayenne.virtualWrite(V5,0);
  //auger
  Cayenne.virtualWrite(V6,0);
  //fan
  Cayenne.virtualWrite(V7,0);

  //indicate no longer running
  Cayenne.virtualWrite(V9,0);
  
}

void ignite()
{
  //indicate ignitor on
  Cayenne.virtualWrite(V5,1);

  //actually start the ignitor
  digitalWrite(1,1);

  //set callback timer
  t.after(10000,ignitor_off);
}

void ignitor_off()
{
  //turn off the ignitor
  digitalWrite(1,0);

  //check temperature
  //alert if not hot enough

  //indicate ignitor off
  Cayenne.virtualWrite(V5,0);
}

void auger_time()
{
  digitalWrite(AugerPin,1);
  Cayenne.virtualWrite(V6,1);
  delay(1000);
  digitalWrite(AugerPin,0);
  Cayenne.virtualWrite(V6,0);
}

Hi, and thanks a lot.

I’m resolved in this way.

// Timer Timer t;


void setup()
{

//set a timer every 2 second call funciotn syncData()
t.every(2000,syncData);

}
void loop()
{

// Update timer t
t.update();

}
void syncData()
{
Cayenne.syncVirtual(VIRTUAL_PIN5);
}

}
CAYENNE_IN(VIRTUAL_PIN5)
{
int currentValue = getValue.asInt(); // 0 to 1
// assuming you wire your relay as normally open
if (currentValue != digitalRead(DIGITAL_INPUT9) ) {
t.pulse(RELAY_DIGITAL_PIN5, 1500, 0); //pin, time(milliseconds), initial state
Cayenne.virtualWrite(VIRTUAL_PIN5, currentValue);
}
else{
Cayenne.virtualWrite(VIRTUAL_PIN5, digitalRead(DIGITAL_INPUT9) );
}
}

It’s work!! The function syncData() recall every 3 second if there is a change of state ( e.g… from physical button ) and assign the value of DIGITAL_INPUT9 on VIRTUAL_PIN5.

Thanks,
Fabio

3 Likes

Super!!

I am confused, is there some piece of the code missing where you constantly call syncData()?

@lanaadvancedsystems for MQTT library the sync function wont work.

1 Like