Exemple #1
0
void Thermocycler::ControlPeltier() {
  Thermocycler::ThermalDirection newDirection = Thermocycler::ThermalDirection::OFF;
  if (iProgramState == ERunning || (iProgramState == EComplete && ipCurrentStep != NULL)) {
    // Check whether we are nearing target and should switch to PID control
    // float plateTemp = GetPlateTemp();
    /* Test: Use estimated sample temp instead or measured well temp */
    float plateTemp = GetTemp();
    if (iPlateControlMode == EBangBang && absf(iTargetPlateTemp - plateTemp) < PLATE_BANGBANG_THRESHOLD) {
      iPlateControlMode = EPIDPlate;
      iPlatePid.SetMode(AUTOMATIC);
      iPlatePid.ResetI();
    }

    // Apply control mode
    if (iPlateControlMode == EBangBang) {
      // Full drive
      iPeltierPwm = (iTargetPlateTemp > plateTemp) ? MAX_PELTIER_PWM : MIN_PELTIER_PWM;
    }
    iPlatePid.Compute();

    if (iDecreasing && iTargetPlateTemp > PLATE_PID_DEC_LOW_THRESHOLD) {
      if (iTargetPlateTemp < plateTemp) {
        iPlatePid.ResetI();
      }
      else {
        iDecreasing = false;
      }
    }

    if (iPeltierPwm > 0)
      newDirection = HEAT;
    else if (iPeltierPwm < 0)
      newDirection = COOL;
    else
      newDirection = OFF;
  }
  else {
    iPeltierPwm = 0;
  }
  iThermalDirection = newDirection;
  SetPeltier(newDirection, iPeltierPwm);
}
Exemple #2
0
void Thermocycler::ControlPeltier() {
  ThermalDirection newDirection = OFF;

  if (iProgramState == ERunning || (iProgramState == EComplete && ipCurrentStep != NULL)) {
    // Check whether we are nearing target and should switch to PID control
    if (iPlateControlMode == EBangBang && absf(iTargetPlateTemp - GetPlateTemp()) < PLATE_BANGBANG_THRESHOLD) {
      iPlateControlMode = EPIDPlate;
      iPlatePid.SetMode(AUTOMATIC);
      iPlatePid.ResetI();
    }

    // Apply control mode
    if (iPlateControlMode == EBangBang)
      iPeltierPwm = iTargetPlateTemp > GetPlateTemp() ? MAX_PELTIER_PWM : MIN_PELTIER_PWM;
    iPlatePid.Compute();

    if (iDecreasing && iTargetPlateTemp > PLATE_PID_DEC_LOW_THRESHOLD) {
      if (iTargetPlateTemp < GetPlateTemp())
        iPlatePid.ResetI();
      else
        iDecreasing = false;
    } 

    if (iPeltierPwm > 0)
      newDirection = HEAT;
    else if (iPeltierPwm < 0)
      newDirection = COOL; 
    else
      newDirection = OFF;
  } 
  else {
    iPeltierPwm = 0;
  }
  iThermalDirection = newDirection;
  SetPeltier(newDirection, abs(iPeltierPwm));
}
void Thermocycler::ControlPeltier() {
  ThermalDirection newDirection = OFF;
  
  if (m_program_state == ERunning || (m_program_state == EComplete && m_current_step != NULL)) {
    // Check whether we are nearing target and should switch to PID control
    if (m_plate_control_mode == EBangBang && fabs(m_target_plate_temp - GetPlateTemp()) < PLATE_BANGBANG_THRESHOLD) {
      m_plate_control_mode = EPIDPlate;
      m_plate_pid->SetMode(AUTOMATIC);
      m_plate_pid->ResetI();
    }
 
    // Apply control mode
    if (m_plate_control_mode == EBangBang)
      m_peltier_pwm = m_target_plate_temp > GetPlateTemp() ? MAX_PELTIER_PWM : MIN_PELTIER_PWM;
    m_plate_pid->Compute();
    
    if (m_is_decreasing && m_target_plate_temp > PLATE_PID_DEC_LOW_THRESHOLD) {
      if (m_target_plate_temp < GetPlateTemp())
        m_plate_pid->ResetI();
      else
        m_is_decreasing = false;
    } 
    
    if (m_peltier_pwm > 0)
      newDirection = HEAT;
    else if (m_peltier_pwm < 0)
      newDirection = COOL; 
    else
      newDirection = OFF;
  } else {
    m_peltier_pwm = 0;
  }
  
  m_thermal_direction = newDirection;
  SetPeltier(newDirection, abs(m_peltier_pwm));
}
Exemple #4
0
// internal
boolean Thermocycler::Loop() {

  ipCommunicator->Process();
  unsigned long loopElapsedTimeMs = millis() - iPrevLoopStartTimeMs;
  iPrevLoopStartTimeMs = millis();

  switch (iProgramState) {
  case EStartup:
    iTempUpdated = false;
    if (millis() > STARTUP_DELAY) {
      iProgramState = EStopped;
      	iRestarted = false;
      if (!iRestarted && !ipCommunicator->CommandReceived()) {
        //check for stored program
        SCommand command;
        /*
        if (ProgramStore::RetrieveProgram(command, (char*)ipCommunicator->GetBuffer())) {
          ProcessCommand(command);
        }
        */
      }
    }
    break;

  case ELidWait:
    if (GetLidTemp() >= iTargetLidTemp - LID_START_TOLERANCE) {
      //lid has warmed, begin program
      iThermalDirection = OFF;
      iPeltierPwm = 0;
      PreprocessProgram();
      iProgramState = ERunning;

      ipProgram->BeginIteration();
      AdvanceToNextStep();

      iProgramStartTimeMs = millis();
    }
    break;

  case ERunning:
    //update program
    if (!iPaused) {
      if (iRamping) {
        // Increment ramping time
        iRampElapsedTimeMs += loopElapsedTimeMs;
      } else {
        // Increment holding time
        iCycleElapsedTimeMs += loopElapsedTimeMs;
      }
      if (iProgramState == ERunning) {
        if (!ipCurrentStep->IsFinal() && (iNextStepPending || iNextCyclePending)) {
          if (iNextStepPending) {
            iNextStepPending = false;
            AdvanceToNextStep();
          }
          if (iNextCyclePending) {
            iNextCyclePending = false;
            AdvanceToNextCycle();
          }
          //check for program completion
          if (ipCurrentStep == NULL || ipCurrentStep->IsFinal()) {
            iProgramState = EComplete;
          }
        } else if (iRamping && abs(ipCurrentStep->GetTemp() - GetTemp()) <= CYCLE_START_TOLERANCE && GetRampElapsedTimeMs() > ipCurrentStep->GetRampDurationS() * 1000) {
          //begin step hold
          //eta updates
          if (ipCurrentStep->GetRampDurationS() == 0) {
            //fast ramp
            iElapsedFastRampDegrees += absf(GetTemp() - iRampStartTemp);
            iTotalElapsedFastRampDurationMs += iRampElapsedTimeMs;
          }

          if (iRampStartTemp > GetTemp()) {
            iHasCooled = true;
          }
          iRamping = false;
          iCycleElapsedTimeMs = 0;
        }
        else if (!iRamping && !ipCurrentStep->IsFinal() && iCycleElapsedTimeMs > (unsigned long)ipCurrentStep->GetStepDurationS() * 1000) {
          //begin next step
          AdvanceToNextStep();
          //check for program completion
          if (ipCurrentStep == NULL || ipCurrentStep->IsFinal()) {
            iProgramState = EComplete;
          }
        }
      }
      break;

    case EComplete:
      PCR_DEBUG_LINE(ipCurrentStep->GetTemp());
      if (iRamping && ipCurrentStep != NULL && abs(ipCurrentStep->GetTemp() - GetTemp()) <= CYCLE_START_TOLERANCE) {
        iRamping = false;
      }
      break;
    }
  }

  statusBuff[statusIndex].timestamp = millis();
  //Read lid and well temp
  statusBuff[statusIndex].hardwareStatus = HARD_NO_ERROR;
  HardwareStatus result = iPlateThermistor.ReadTemp();
  if (result!=HARD_NO_ERROR) {
      statusBuff[statusIndex].hardwareStatus = result;
  }
  result = iLidThermistor.ReadTemp();
  if (result!=HARD_NO_ERROR) {
      statusBuff[statusIndex].hardwareStatus = result;
  }

  statusBuff[statusIndex].lidTemp = GetLidTemp();
  statusBuff[statusIndex].wellTemp = GetPlateTemp();

  float lidTemp = 0;
  float wellTemp = 0;

  CheckHardware(&lidTemp, &wellTemp);
  PCR_DEBUG("L=");
  PCR_DEBUG(lidTemp);
  PCR_DEBUG(" W=wellTemp");
  PCR_DEBUG_LINE(wellTemp);

  iLidThermistor.setTemp(lidTemp);
  iPlateThermistor.setTemp(wellTemp);

  double estimatedAirTemp = wellTemp * 0.4 + lidTemp * 0.6;
  // Estimated delta to next 1 sec
  double diff = ((wellTemp - iEstimatedSampleTemp)/THETA_WELL + (estimatedAirTemp-iEstimatedSampleTemp)/THETA_LID ) / CAPACITY_TUBE;

  if (!iTempUpdated) {
    iTempUpdated = true;
    iEstimatedSampleTemp = estimatedAirTemp;
  } else if ( 5>diff && diff > -5) {
    iEstimatedSampleTemp += diff;
  }

  CalcPlateTarget();

  // Check error
  //if (iHardwareStatus==HARD_NO_ERROR || true) { //TODO WELL_TEST (dummy line)
  if (iHardwareStatus==HARD_NO_ERROR) { //TODO WELL_TEST
      ControlLid();
      ControlPeltier();
      if (iHardwareStatus!=HARD_NO_ERROR) {
        PCR_DEBUG("ERR=");
        PCR_DEBUG_LINE(iHardwareStatus);
      }
  } else {
      PCR_DEBUG_LINE("ALL OFF");
      iProgramState = EError;
      SetPeltier(OFF, 0);
      SetLidOutput(0);
  }

  //program
  UpdateEta();
 #ifdef USE_LCD
  ipDisplay->Update();
 #endif
  statusIndex = (statusIndex+1) % CyclerStatusBuffSize;
  statusCount++;
  return true;
}