standalone operation of Metro M0 + Arduino

Post here about your Arduino projects, get help - for Adafruit customers!

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
jmtatum
 
Posts: 20
Joined: Tue Oct 03, 2017 10:49 pm

standalone operation of Metro M0 + Arduino

Post by jmtatum »

I have a Metro M0 successfully running a sketch and interfaces well with the other boards (Wifi and stepper). I am having a problem with two things that are probably related:
1) When I take the reset pin to low, the sketch stops but the system does not reset; and
2) when I unplug the USB and cycle power, the sketch doesn't automatically start

I would like this to run as a standalone item (like the Uno did)...any suggestions?

User avatar
Franklin97355
 
Posts: 23910
Joined: Mon Apr 21, 2008 2:33 pm

Re: standalone operation of Metro M0 + Arduino

Post by Franklin97355 »

It should run unconnected. Is the code the same on the Uno or are you running a different sketch? Are you using the serial port in your code? Could you post your code so we can try it to see if the problem lies there? Please use code tags when posting code or logs to the forums. It preserves formatting and makes it easier for everyone to read the code. Click the code button above the reply box and past your code between the tags created.

User avatar
jmtatum
 
Posts: 20
Joined: Tue Oct 03, 2017 10:49 pm

Re: standalone operation of Metro M0 + Arduino

Post by jmtatum »

Thanks for the reply! It is a lot of code, and yes it worked on the Uno with no major changes to switch to the Metro. I followed the Adafruit tutorial and set it up fine.

I'll try to run a simple example sketch (like blink) and see if it works disconnected from the USB.

Thinking of it that way, is there anything different I would need to do with Metro to allow Blink to run standalone? I read over the Bootloader methods, but that seemed to be for Python use.

User avatar
jmtatum
 
Posts: 20
Joined: Tue Oct 03, 2017 10:49 pm

Re: standalone operation of Metro M0 + Arduino

Post by jmtatum »

So, I uploaded the Blink example, and the Metro M0 restarted fine using my hardware reset button (which takes the reset pin low), and also with cycling power. So, you were right, it must be something in the code...

Here is my code. The application is for 2 motors on a wood stove btw...

Code: Select all

/*************************************************** 

 ****************************************************/

#include <SPI.h>
#include "Adafruit_MAX31855.h"                    //library for the digital temp controller
#include <Wire.h>                                 //copied from motor example
#include <Adafruit_MotorShield.h>                 // motor shield library
#include "utility/Adafruit_MS_PWMServoDriver.h"   //motor driver
#include <PID_v1.h>   
// #include <EEPROM.h>    //****REMOVED FOR METRO
#include <Adafruit_RGBLCDShield.h>                //LCD display drivers
#include <utility/Adafruit_MCP23017.h>
#include <WiFi101.h>
#include <BlynkSimpleWiFiShield101.h>
#include <FlashStorage.h>

// Wifi info for Blynk data ******************************************//
char auth[] = “#####BLYNKTOKEN#########”;

// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = “*****”;
char pass[] = “#####“;
BlynkTimer timer;
// This function will be called every time Slider Widget
// in Blynk app writes values to the Virtual Pin V4
int SliderTemp;

BLYNK_WRITE(V4)
{
  SliderTemp = param.asInt(); // assigning incoming value from pin V4 to a variable
}

BLYNK_CONNECTED() {
  // Request Blynk server to re-send latest values for all pins
  //Blynk.syncAll();

  // You can also update individual virtual pins like this:
  Blynk.syncVirtual(V4);
}

// Wifi info for Blynk data ******************************************//


//#define BLYNK_PRINT Serial
#define MAXDO   0  // these are for the temp
#define MAXCS   1
#define MAXCLK  2
#define RESETBUTTON 3   // button to store motor positions, then halt

//Define Variables we'll be connecting to for PID controller

double ExSetpoint, ExInput, ExOutput; 
double DoorInput, DoorOutput;
double DoorSetpoint=39;   //Setpoint for Door is the exhaust position
int DoorMultiple = 5; //number for increment to calculate

//Define stepper positioning. Exhaust fully shut is 0 step, fully open is 50.  
int ExhaustPosition = 0;
FlashStorage(ExhaustPosition_storage, int);
int DoorPosition = 50;   // Want to start 50 is open
int StepsToMove = 0;
int DoorStepsToMove = 0;
int Counter = DoorMultiple - 1;
int rst = 1;   //variable to track reset button; HIGH is not pushed and runs normally
int runprogram = 1; //variable to track reset state


//Specify the links and initial tuning parameters
double ExKp=1, ExKi=2, ExKd=.01;
double DKp=.5, DKi=1, DKd=.01;
PID ExPID(&ExInput, &ExOutput, &ExSetpoint, ExKp, ExKi, ExKd, P_ON_M, DIRECT);
PID DoorPID(&DoorInput, &DoorOutput, &DoorSetpoint, DKp, DKi, DKd, P_ON_M, REVERSE);

// initialize the Thermocouple
Adafruit_MAX31855 thermocouple(MAXCLK, MAXCS, MAXDO);
// setup variables for averaging thermocouple readings
const int numReadings = 10;     // number of readings to average
double readings[numReadings];      // the readings from the thermocouple
int readIndex = 0;              // the index of the current reading
double total = 0;                  // the running total
double average = 0;                // the average

// setup time variable for averaging temp
unsigned long intervalAvg=100; // the time we need to wait
unsigned long previousMillisAvg=0; // millis() returns an unsigned long.
unsigned long intervalPID=3000; // interval to calculate PID
unsigned long previousMillisPID=0; //used to store the previous time for comparison
 
// initialize the motor
Adafruit_MotorShield AFMS = Adafruit_MotorShield(); 
Adafruit_StepperMotor *myMotor1 = AFMS.getStepper(200, 1);
Adafruit_StepperMotor *myMotor2 = AFMS.getStepper(200, 1);

//initialize the display
Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();
#define OFF 0x0
#define ON 0x1
// LCD display is 16x2

// Main menu array
String mainMenu[10]; 
// Main menu default choice
int mainSwitch = 0;  
int mainSwitchOld = 0;
// Amount of items in our main menu.
// Can be found by formula:
// (Amount_of_items - 1)
#define mainMenuItems 8
uint8_t buttons = 0; // variable to read the buttons
int InMainMenu = 0;  // variable set to 1 when in Main Menu
int CursorPosit = 1; // position of cursur in the menus

void myTimerEvent()
{
  // You can send any value at any time.
  // Please don't send more that 10 values per second.
  Blynk.virtualWrite(V0, (int)ExInput);          // send out the temperature input
  Blynk.virtualWrite(V1, (int)ExSetpoint);       // send out the temperature setpoint
  Blynk.virtualWrite(V2, ExhaustPosition);  // send out the exhaust position
  Blynk.virtualWrite(V3, DoorPosition);  // send out the door position
  
}


void setup() {
  while (!Serial); // wait for Serial on Leonardo/Zero, etc
  Serial.begin(9600);

  Blynk.begin(auth, ssid, pass);
  //setup a function to be called to send data to Blynk
  timer.setInterval(5000L, myTimerEvent);
  
  // Setup LCD
  lcd.begin(16, 2);
  lcd.setBacklight(ON);
  lcd.clear();
  lcd.setCursor(0,1);
  lcd.print("Startup ");
  lcd.setCursor(0,0);

  // Filling our main menu items
  mainMenu[0] = "Setpoint        ";  //temp setpoint
  mainMenu[1] = "Set Exhaust shut";  //exhaust damper Position
  mainMenu[2] = "Set Door shut   ";  //door damper position
  mainMenu[3] = "Exhaust Kp      ";
  mainMenu[4] = "Exhaust Ki      ";
  mainMenu[5] = "Exhaust Kd      ";
  mainMenu[6] = "Door Kp         ";
  mainMenu[7] = "Door Ki         ";
  mainMenu[8] = "Door Kd         ";
  
  // wait for MAX chip to stabilize
  delay(500);

  AFMS.begin();   // motor shield
  myMotor1->setSpeed(5);  // 5 rpm 
  myMotor2->setSpeed(5);
  ExhaustPosition = ExhaustPosition_storage.read(); //set exhaust position to stored value
  DoorPosition = 50;
  DoorStepsToMove = 50 - DoorPosition;
  myMotor1->step(ExhaustPosition, BACKWARD, SINGLE); //return to start position zero
  myMotor2->step(DoorStepsToMove, BACKWARD, SINGLE);
  ExhaustPosition = 0;
  DoorPosition = 50;
  DoorStepsToMove = 0;
  delay(3000);
  
    //initialize the variables we're linked to
  ExInput = thermocouple.readFarenheit();
  DoorInput = ExhaustPosition;
  //ExSetpoint = 85.0;
  

  //turn the PID on
  ExPID.SetMode(AUTOMATIC);
  ExPID.SetOutputLimits(0, 50);
  DoorPID.SetMode(AUTOMATIC);
  DoorPID.SetOutputLimits(0, 50);

  //setup reset button
  pinMode(RESETBUTTON, INPUT);   // used to set GPIO to store setpoint and exhaust position

  //clear out average temps
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    readings[thisReading] = 0;
  }

  //set PID previous time to now
  previousMillisPID = millis();
  lcd.clear();
}

void loop() {

  // send data to blynk;
  Blynk.run();
  timer.run(); //initiates Blynktimer
 
  unsigned long currentMillis = millis(); // grab current time

  rst = digitalRead(RESETBUTTON);  //Check the reset button.  If pressed, it goes low.
  buttons = lcd.readButtons();  //Check buttons
  ExSetpoint = SliderTemp; // setpoint for temp to match with slider in Blynk

//MENU & SETUP SECTION
  if (buttons & BUTTON_SELECT) {    // go into the Main Menu when Select is pressed
    InMainMenu=HIGH;
    while(lcd.readButtons()) { }    // waits for buttons to release
    MainMenuDisplay();              // diplays the first menu item
    while(InMainMenu) {             // while we are in the Main Menu...

// this section gets called repeatedly when no buttons are pressed.  We are 
// checking for buttons to be pushed, and then will scroll or take action.      
      
      buttons = lcd.readButtons();  // fills buttons with state of LCD buttons
      MainMenuBtn();                // checks to see if up or down was selected, and scrolls 
      if(buttons & BUTTON_SELECT)   //enter selected menu if select button is pressed
      {
        switch (mainSwitch)
        {
            case 0:
              Setpoint();
              MainMenuDisplay();
              break;
            case 1:
              SetEx();
              MainMenuDisplay();
              break;
            case 2:
              SetDoor();
              MainMenuDisplay();
              break;
            case 3:
              ExhaustKp();
              MainMenuDisplay();
              break;
            case 4:
              ExhaustKi();  //Exhaust Ki
              MainMenuDisplay();
              break;
            case 5:
              ExhaustKd();  //Exhaust Kd
              MainMenuDisplay();
              break;
            case 6:
              DoorKp();  //Door Kp
              MainMenuDisplay();
              break;
            case 7:
              DoorKi();  //Door Ki
              MainMenuDisplay();
              break;
            case 8:
              DoorKd();  //Door Kd
              MainMenuDisplay();
              break;
        }
        while(lcd.readButtons()) { }   // waits for buttons to release after exit from submenu
      }   // after this IF statement, back in the MainMenu.  check for left to exit Main Menue
          
      if(lcd.readButtons()==BUTTON_LEFT) { //Check left button to leave Main Menu
        Serial.print("Trying to leave");
        mainSwitch=0;
        mainSwitchOld=0;
        InMainMenu=0;
      }
          
      
      
    }  //  end of the Main Menu WHILE statement, check to see if we can exit
    lcd.clear();  //clear screen, allow operating menu to write to LCD
  }



// MEASURE & AVERAGE TEMP SECTION  
// this section reads temp and calculates a running average of temp, after a certain interval
  if ((unsigned long)(currentMillis - previousMillisAvg) >= intervalAvg) {
     // subtract the oldest reading:
    total = total - readings[readIndex];
    // read from the sensor:
    readings[readIndex] = thermocouple.readFarenheit();
    while (isnan(readings[readIndex])) {
      delay (5);
      lcd.setCursor(8,1);
      lcd.print("n");
      readings[readIndex] = thermocouple.readFarenheit();
      }
  
    // add the reading to the total:
    total = total + readings[readIndex];
    // advance to the next position in the array:
    readIndex = readIndex + 1;
  
    // if we're at the end of the array...
    if (readIndex >= numReadings) {
      // ...wrap around to the beginning:
      readIndex = 0;
    }
  
  // calculate the average:
  average = total / numReadings;  
  ExInput = average;   

  lcd.setCursor(0,0);
  lcd.print("SP=");
  lcd.setCursor(3,0);
  lcd.print(ExSetpoint);
  lcd.setCursor(0,1);
  lcd.print(" T=");
  lcd.setCursor(3,1);
  lcd.print(ExInput);
  lcd.setCursor(8,1);
  lcd.print(" ");
  
  previousMillisAvg = millis();
  }

// CALCULATE PID / MOVE exhaust SECTION (if reset hasn't been pushed)
// this section calculates the PID after a certain amount of time (interval PID)
  if ((unsigned long)(currentMillis - previousMillisPID) >= intervalPID) {
    ExPID.Compute();
    Counter++;   // count the number of times we calculate exhaust PID to trigger door PID
    Serial.print(Counter);
    Serial.print(" ");
    Serial.print(DoorMultiple);
    Serial.print(" ");
    
  if (Counter == DoorMultiple) {
    DoorInput = ExhaustPosition;
    DoorPID.Compute();
    Counter = 0;
    DoorStepsToMove = DoorOutput-DoorPosition;
      
  }


    if (rst == HIGH and runprogram == HIGH) { 
      StepsToMove = ExOutput - ExhaustPosition;
      //Serial.print(StepsToMove);
      //Serial.print(" ");
      if (StepsToMove > 0) { 
        myMotor1->step(StepsToMove, FORWARD, SINGLE); }
      else {
        if (StepsToMove < 0) { 
          myMotor1->step(-StepsToMove, BACKWARD, SINGLE);
          } 
        }
     ExhaustPosition = ExhaustPosition + StepsToMove;


      //move DoorMotor
      if (DoorStepsToMove > 0) { 
        myMotor2->step(DoorStepsToMove, FORWARD, SINGLE);
        DoorPosition = DoorPosition + DoorStepsToMove;
        DoorStepsToMove = 0; 
        
          }
      else {
        if (DoorStepsToMove < 0) { 
          myMotor2->step(-DoorStepsToMove, BACKWARD, SINGLE);
          DoorPosition = DoorPosition + DoorStepsToMove;
          DoorStepsToMove = 0;
          } 
        }
          // Print Exhaust Position to LCD
     Serial.print(ExhaustPosition);
     Serial.print(" ");
     lcd.setCursor(11,0);
     lcd.print("EP=");
     lcd.setCursor(14,0);
     if (ExhaustPosition < 10) {
      lcd.print(ExhaustPosition);
      lcd.print(" "); }
      else {
        lcd.print(ExhaustPosition);  }

             // Print Door Position to LCD
     Serial.print(DoorPosition);
     Serial.print(" ");
     lcd.setCursor(11,1);
     lcd.print("DP=");
     lcd.setCursor(14,1);
     if (DoorPosition < 10) {
      lcd.print(DoorPosition);
      lcd.print(" "); }
      else {
        lcd.print(DoorPosition);  }

     
      }
   
    Serial.println(" ");
    previousMillisPID = millis();
 }

//STORE VARIABLES FOR NEXT TIME
// if reset button is pushed, store exhaust position and setpoint and then halt motor movement
 if (rst == LOW and runprogram == HIGH) {
  ExhaustPosition_storage.write(ExhaustPosition);
  // removed for METRO*** EEPROM.write(1, ExSetpoint);
  runprogram = LOW;
  lcd.clear();
  lcd.print("Exiting");
 }

  
  delay(5);

}


void Setpoint()
{  
    if(ExSetpoint>=100) CursorPosit = 2; 
    else CursorPosit = 1;
    
    lcd.setCursor(CursorPosit,1);
    lcd.blink();
    while(lcd.readButtons()) {}   // waits for buttons to release
    
    while(lcd.readButtons()!=BUTTON_LEFT)
    {
        if(lcd.readButtons() == BUTTON_UP) {
          ExSetpoint++;
          delay(250);
          lcd.setCursor(0,1);
          lcd.print((int)ExSetpoint); 
          lcd.print(" ");
          if(ExSetpoint>=100) CursorPosit = 2; 
          else CursorPosit = 1;
          lcd.setCursor(CursorPosit,1); 
        }
        else if(lcd.readButtons() == BUTTON_DOWN) {
          ExSetpoint--;
          delay(250);
          lcd.setCursor(0,1);
          lcd.print((int)ExSetpoint);
          lcd.print(" ");
          if(ExSetpoint>=100) CursorPosit = 2; 
          else CursorPosit = 1;
          lcd.setCursor(CursorPosit,1); 
        }
        //adjust for up and down, save as new Setpoint
    }
    lcd.noBlink();
    Blynk.virtualWrite(V4, ExSetpoint);
    SliderTemp=ExSetpoint;
    
}
void SetEx()
{  
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("DP:move to close");
    lcd.setCursor(0,1);
    lcd.print("Enter to set");

    while(lcd.readButtons()) { }   // waits for buttons to release
    
    while(lcd.readButtons()!= BUTTON_LEFT)
    {
        //Insert Task for moving DP motor
    }
    
}
void SetDoor()
{  
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("No motor for now");
    
    while(lcd.readButtons()) {
      Serial.print("Waiting..");
    }   // waits for buttons to release
    
    while(lcd.readButtons()!= BUTTON_LEFT)
    {
        //Insert Task for Menu C here
    }
    
}
void ExhaustKp()
{  
    if(ExKp>=10) CursorPosit = 3; 
    else CursorPosit = 2;
    
    lcd.setCursor(CursorPosit,1);
    lcd.blink();
    while(lcd.readButtons()) {}   // waits for buttons to release
    
    while(lcd.readButtons()!=BUTTON_LEFT)
    {
        if(lcd.readButtons() == BUTTON_UP) {
          ExKp = ExKp + .1;
          delay(250);
          lcd.setCursor(0,1);
          lcd.print(ExKp); 
          lcd.print(" ");
          if(ExKp>=10) CursorPosit = 3; 
          else CursorPosit = 2;
          lcd.setCursor(CursorPosit,1); 
        }
        else if(lcd.readButtons() == BUTTON_DOWN) {
          ExKp = ExKp - .1;;
          delay(250);
          lcd.setCursor(0,1);
          lcd.print(ExKp);
          lcd.print(" ");
          if(ExKp>=10) CursorPosit = 3; 
          else CursorPosit = 2;
          lcd.setCursor(CursorPosit,1); 
        }
        //adjust for up and down, save as new Setpoint
    }
    lcd.noBlink();  //stop blinking when exit to main menu
}

void ExhaustKi()
{  
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Set Exhaust Ki  ");

    while(lcd.readButtons()) {
      Serial.print("Waiting..");
    }   // waits for buttons to release
    
    while(lcd.readButtons()!= BUTTON_LEFT)
    {
        //Insert Task for Menu D here
    }
    
}

void ExhaustKd()
{  
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Set Exhaust Kd  ");

    while(lcd.readButtons()) {
      Serial.print("Waiting..");
    }   // waits for buttons to release
    
    while(lcd.readButtons()!= BUTTON_LEFT)
    {
        //Insert Task for Menu D here
    }
    
}

void DoorKp()
{  
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Set Door Kp  ");
   
    while(lcd.readButtons()) {
      Serial.print("Waiting..");
    }   // waits for buttons to release
    
    while(lcd.readButtons()!= BUTTON_LEFT)
    {
        //Insert Task for Menu D here
    }
    
}

void DoorKi()
{  
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Set Door Ki  ");

    while(lcd.readButtons()) {
      Serial.print("Waiting..");
    }   // waits for buttons to release
    
    while(lcd.readButtons()!= BUTTON_LEFT)
    {
        //Insert Task for Menu D here
    }
    
}

void DoorKd()
{  
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Set Door Kd  ");

    while(lcd.readButtons()) {
      Serial.print("Waiting..");
    }   // waits for buttons to release
    
    while(lcd.readButtons()!= BUTTON_LEFT)
    {
        //Insert Task for Menu D here
    }
    
}

void MainMenuDisplay()
{
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print(mainMenu[mainSwitch]);
    lcd.setCursor(0,1);
    switch (mainSwitch)
        {
            case 0:   // Setpoint
              lcd.print((int)ExSetpoint);
              break;
            case 1:  // Set Exhaust exhaust position
              lcd.print("Position=");
              lcd.print(ExhaustPosition);
              break;
            case 2:
              // Set Inlet damper position
              lcd.print("Position=");
              lcd.print("n/a"); // not connected yet
              break;
            case 3:
              lcd.print(ExKp);  //Exhaust Kp
              break;
            case 4:
              lcd.print(ExKi);  //Exhaust Ki
              break;
            case 5:
              lcd.print(ExKd);  //Exhaust Kd
              break;
            case 6:
              lcd.print(DKp);  //Door Kp
              break;
            case 7:
              lcd.print(DKi);  //Door Ki
              break;
            case 8:
              lcd.print(DKd);  //Door Kd
              break;       
        }
}

void MainMenuBtn()   //This program scrolls the menu up and down only
{
    //Serial.print("In MainMenuBtn");
    //Serial.println(" ");
    
    //buttons = lcd.readButtons();
    if(buttons & BUTTON_DOWN)
    {
        Serial.print("BUTTON DOWN");
        Serial.println(" ");
        mainSwitch++;
        if(mainSwitch > mainMenuItems)
          mainSwitch = 0;
        while(lcd.readButtons()) {
           Serial.print("Waiting..");
        }   // waits for buttons to release
    }
    else if(buttons & BUTTON_UP)
    {
        mainSwitch--;
        if(mainSwitch < 0)
          mainSwitch = mainMenuItems;  
        while(lcd.readButtons()) {
           Serial.print("Waiting..");
        }   // waits for buttons to release  
    }
   
    if(mainSwitch != mainSwitchOld) //only update display when page change
    {
        MainMenuDisplay();
        mainSwitchOld = mainSwitch;
    }
}

When I reload this code and try the reset button, everything stops and there are no light indications that anything is happening. The Serial monitor I use for debugging stops as well. Also, cycling power does not work either...

Here are some troubleshooting scenarios, assuming USB is plugged in, and DC power connected (12V):
-unplug USB, everything still works
-cycle power, green light comes on but no autostart

Thanks in advance!

User avatar
Franklin97355
 
Posts: 23910
Joined: Mon Apr 21, 2008 2:33 pm

Re: standalone operation of Metro M0 + Arduino

Post by Franklin97355 »

Code: Select all

  while (!Serial); // wait for Serial on Leonardo/Zero, etc
If you are waiting for serial and not connected to serial what happens here?

User avatar
jmtatum
 
Posts: 20
Joined: Tue Oct 03, 2017 10:49 pm

Re: standalone operation of Metro M0 + Arduino

Post by jmtatum »

That's it, many thanks! This was leftover from some examples I used, and never caused a problem with the Uno. Not sure why it would cause a halt with the Metro, but that did it!
Thanks for taking the time.

Locked
Please be positive and constructive with your questions and comments.

Return to “Arduino”