PID for ramp temperature control using LM35 temperature sensor and two actuators, a bulb and a ventilator.

Dependencies:   DebouncedIn QEI TextLCD_encoder mbed

Fork of PID_Encoder by Gustavo Ramirez

Files at this revision

API Documentation at this revision

Comitter:
Gregorio
Date:
Thu Jun 12 16:43:28 2014 +0000
Parent:
4:d42fe3777735
Commit message:
PID for ramp temperature control using LM35 temperature sensor and two actuators, a bulb and a ventilator.

Changed in this revision

main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
diff -r d42fe3777735 -r ad88703dc6d3 main.cpp
--- a/main.cpp	Sat May 24 15:05:40 2014 +0000
+++ b/main.cpp	Thu Jun 12 16:43:28 2014 +0000
@@ -1,248 +1,517 @@
 #include "mbed.h"
 #include "DebouncedIn.h"
 #include "TextLCD.h"
-#include "QEI.h" 
+#include "QEI.h"
 
-TextLCD lcd(PTB10, PTB11, PTE2, PTE3, PTE4, PTE5); // rs, e, d4-d7
-QEI wheel (PTD5, PTD0, NC, 100);
-AnalogIn y(PTB1);
-AnalogOut u(PTE30);
-DigitalOut led1(LED1);
-DigitalOut led2(LED2);
-DigitalOut led3(LED3);
-DebouncedIn button1(PTC12);
-DebouncedIn button2(PTC13);
-DebouncedIn button3(PTC16);
-DebouncedIn button4(PTC17);
+TextLCD lcd(PTB10, PTB11, PTE2, PTE3, PTE4, PTE5); // rs, e, d4-d7 entradas del LCD
+QEI wheel (PTD5, PTD0, NC, 100); // entradas para el ENCODER
+AnalogIn LM(PTB1); // temperatura leida por un LM35
+PwmOut bomb(PTD4); //salida para activar un bombillo
+PwmOut vent(PTA12); //salida para activar un ventilador
+DigitalOut led3(LED3); // Led para indicar que los botones se presionan
+DebouncedIn button3(PTC16); //boton 3 es el pulsador del ENCODER
+DebouncedIn button4(PTA13); // boton 4 es para la configuración de las constantes del pid, de tiempo de rampa y de temperatura del horno
 
-      //codigos movimiento del curzor
-      //18 para izquierda
-      //1A para derecha
-
-//int C1=0x0E; // solo muestra el curzor
+//codigos movimiento del curzor LCD
 int C2=0x18; // desplaza izquierda
 int C3=0x1A; // desplaza derecha
 int C4=0x0C; // quito cursor bajo
-int C1=0x0F;
+int C1=0x0F; // solo muestra el curzor
+
 
-int i; // indice de la variable
-int j; //variable controla cambio 4 posiciones
-int kp, ki, kd, sp;
-float yr, ap, ai, ad, err, med, err_v, cycle, pid; 
-int b=0; 
+//definicion de varibles
+int i=1; // i controla cambio de "modo" del boton 4. i=1 -> (constantes PID), i=2 -> (Tiempo y temperatura)
+int j=1; //j controla cambio de posiciones para cambiar variable (con el boton del ENCODER)
+int kp=40, ki=40, kd=40, sp; //constantes del pid y del setpoint
+int t1=60, t2=60, t3=60; //tiempos de la rapmpa de subida, temperatura estable y tiempo de bajada t1=t2=t3 [segundos]
+int temp=45; // temp:temperatura final a la que llega el horno[ºC]
+float ta, avg, a[10]; // ta: temperatura ambiente que se lee con el LM35, antes de empezar control, avg, promedio de temperatura para minimizar error
+float yr, ap, ai, ad, err, med, err_v, cycle, pid; //
+float dt1, dt2, dt3,c=0; // variables para ciclos de control en tiempos t1, t2 y t3
+int b=0; // Se usa para imprimir datos en la LCD
 float pidn;
 Timer t;
 
-int main() {
-    lcd.cls();
-    lcd.writeCommand(C1);//escribimos un comando segun el manual del modulo LCD
-    lcd.locate(0,0);
-    lcd.printf("Sp%d",sp);
-    lcd.locate(8,0);
-    lcd.printf("Kp%d",kp);
-    lcd.locate(0,1);
-    lcd.printf("Ki%d",ki);
-    lcd.locate(8,1);
-    lcd.printf("Kd%d",kd);
-          
+// Inicia el código
+int main()
+{
     while(1) {
-    
-              led3 =1;
-           if (button3.falling()) {   //INCREMENTA POSICION DEL MENU CON BOTON 3 (Switche encoder)
-              led3 =!led3;              
-               ++j;
-               }    
-                                                
-           if (j==0){      
-               sp=sp+wheel.getPulses();
-               wheel.reset();
-                 if (sp>999){
-                       sp=999;
-                            } 
-                 if (sp<0){
-                       sp=0;
-                          }   
-               lcd.locate(2,0);
-               lcd.printf("     ",sp);
-               lcd.locate(2,0);
-               lcd.printf("%d",sp);
-               wait(0.2);
-               
-               if(button3.falling()){
-                 j=1;
-                 led3=0;
-                 wait(0.3);
-                 wheel.reset();   
-                                    }   
-                   
-                     }
-              
-           if (j==1) {
-               kp=kp+wheel.getPulses();
-               wheel.reset();
-                 if (kp>999){
-                     kp=999;
-                          }              
-                 if (kp<0){
-                     kp=0;
-                          }   
-               lcd.locate(10,0);
-               lcd.printf("     ");
-               lcd.locate(10,0);
-               lcd.printf("%d",kp);
-               wait(0.2);
-               
-               if(button3.falling()){
-                 j=2;
-                 led3=0;
-                 wait(0.3);
-                 wheel.reset();    
-                                    }
-                                           
-                      }
-              
-           if (j==2) {
-               ki=ki+wheel.getPulses();
-               wheel.reset();
-                 if (ki>999){
-                     ki=999;
-                          }              
-                 if (ki<0){
-                     ki=0;
-                          }   
-               lcd.locate(2,1);
-               lcd.printf("     ");
-               lcd.locate(2,1);
-               lcd.printf("%d",ki);
-               wait(0.2);
-               
-               if(button3.falling()){
-                 j=3;
-                 led3=0;
-                 wait(0.3);
-                 wheel.reset();
-                                    }
-                                    
-                     }
-                     
-           if (j==3) {
-               kd=kd+wheel.getPulses();
-               wheel.reset();
-                 if (kd>999){
-                     kd=999;
-                            }              
-                 if (kd<0){
-                     kd=0;
-                          }   
-               lcd.locate(10,1);
-               lcd.printf("     ");
-               lcd.locate(10,1);
-               lcd.printf("%d",kd);
-               wait(0.2);
-               
-               if(button3.falling()){
-                 j=0;
-                 led3=0;
-                 wait(0.3);
-                 wheel.reset();
-                                    }
-                                            
-                       } 
-            
-           if (j==4) {
-               j=0;
-                     }                          
-                     
-           if (!button4){
-           break;        //sale del bucle si pisan suiche4
-                        }                  
-              }          //cierro while(1)
-//%---------------------------------------------------------------------              
-              
-              
-          lcd.writeCommand(C4);//escribimos un comando segun el manual del modulo LCD para quitar cursor bajo
-           lcd.cls(); //borra la pantalla
-           lcd.printf("GUARDAMOS \nVALORES |m|"); 
-           wait(2);
-           
-           // se imprimen los parches del control  *****************************************
-           lcd.cls();
-           lcd.printf("Er%d",err);
-           lcd.locate(8,0);
-           lcd.printf("Me%d",med);
-           lcd.locate(0,1);
-           lcd.printf("Sp%d",sp);
-           lcd.locate(8,1);
-           lcd.printf("Co%d",pid);
-           wait(1); 
-           
-           
-           // CICLO PRINCIPAL CONTROLADOR PID
-           b=0;
-           while(1) {
-           med=1000*y.read();                   //leer puerto analogo y asignar a med
-           err = (sp-med);
-           ap = kp*err;
-           
-           // se verifica que la accion integral no sea muy grande
-           if(ai<100)
-           {
-              ai =(ki*err)+ai;    //calculo de la integral del error
-           }
-           //else{
-                //Dejo de sumar la accion integral
-             //  }
-                      
-           ad = kd*(err-err_v); //calculo de la accion derivativa
-           pid = (ap+ai+ad);
-           
-              
-                
-           // se verifica que pid sea positivo **************************************
-           if(pid<=0){pid=0;}
-           // se verifica que pid sea menor o igual la valor maximo *****************
-           if(pid>=1000){pid=1000;}
-                      
-           // se actualizan las variables *******************************************
-           err_v = err;         
-           
-           //se muestran las variables******************************************
-           
-           if (b==0)
-           {
-            t.start();
-            b=1;
-           }
-        if(t>=0.01)
-        {
-           wait(0.2);
-           lcd.locate(2,0);
-           lcd.printf("    ");
-           lcd.locate(2,0);
-           lcd.printf("%d",err);
-           lcd.locate(10,0);
-           lcd.printf("    ");
-           lcd.locate(10,0);
-           lcd.printf("%d",med);
-           lcd.locate(2,1);
-           lcd.printf("    ");
-           lcd.locate(2,1);
-           lcd.printf("%d",sp);
-           lcd.locate(10,1);
-           lcd.printf("    ");
-           lcd.locate(10,1);
-           lcd.printf("%d",pid);
-        }  
-           
-           //Normalizacion de la salida
-           pidn=pid/1000;
-           //  se envia el valor pid a puerto analogico de salida (D/A) **************
-           u.write(pidn);
-          
-           //  se repite el ciclo
-           wait(0.1);
-           }
-           
-                      
+        if(i==1) {
+            lcd.cls();
+            lcd.writeCommand(C1);//escribimos un comando segun el manual del modulo LCD
+            lcd.locate(0,0);
+            lcd.printf(" PID");
+            lcd.locate(8,0);
+            lcd.printf("Kp:%d",kp);
+            lcd.locate(0,1);
+            lcd.printf("Ki:%d",ki);
+            lcd.locate(8,1);
+            lcd.printf("Kd:%d",kd);
+
+            led3 =1;
+            if (button3.falling()) {  //INCREMENTA POSICION DEL MENU CON BOTON 3 (Switche encoder)
+                led3 =!led3;
+            }
+
+            if (j==1) {
+                kp=kp+wheel.getPulses();
+                wheel.reset();
+                if (kp>999) {
+                    kp=999;
+                }
+                if (kp<0) {
+                    kp=0;
+                }
+                lcd.locate(11,0);
+                lcd.printf("     ");
+                lcd.locate(11,0);
+                lcd.printf("%d",kp);
+                wait(0.2);
+
+                if(button3.falling()) {
+                    j=2;
+                    led3=0;
+                    wait(0.3);
+                    wheel.reset();
+                }
+            }
+            if (j==2) {
+                ki=ki+wheel.getPulses();
+                wheel.reset();
+                if (ki>999) {
+                    ki=999;
+                }
+                if (ki<0) {
+                    ki=0;
+                }
+                lcd.locate(3,1);
+                lcd.printf("     ");
+                lcd.locate(3,1);
+                lcd.printf("%d",ki);
+                wait(0.2);
+
+                if(button3.falling()) {
+                    j=3;
+                    led3=0;
+                    wait(0.3);
+                    wheel.reset();
+                }
+            }
+            if (j==3) {
+                kd=kd+wheel.getPulses();
+                wheel.reset();
+                if (kd>999) {
+                    kd=999;
+                }
+                if (kd<0) {
+                    kd=0;
+                }
+                lcd.locate(11,1);
+                lcd.printf("     ");
+                lcd.locate(11,1);
+                lcd.printf("%d",kd);
+                wait(0.2);
+
+                if(button3.falling()) {
+                    j=1;
+                    led3=0;
+                    wait(0.3);
+                    wheel.reset();
+                }
+            }
+            if (j==4) {
+                j=1;
+            }
+            if(button4.falling()) {
+                i=2;
+                j=0;
+            }
+        }//CIERRA EL MODO i=1 -> constantes PID
+        if(i==2) { //INICIA EL MODO i=2 -> tiempos y temperatura
+            lcd.cls();
+            lcd.writeCommand(C1);//escribimos un comando segun el manual del modulo LCD
+            lcd.locate(0,0);
+            lcd.printf("T1:%d",t1);
+            lcd.locate(8,0);
+            lcd.printf("T2:%d",t2);
+            lcd.locate(0,1);
+            lcd.printf("T3:%d",t3);
+            lcd.locate(8,1);
+            lcd.printf("Tm:%d",temp);
+
+
+            led3 =1;
+            if (button3.falling()) {  //INCREMENTA POSICION DEL MENU CON BOTON 3 (Switche encoder)
+                led3 =!led3;
+            }
+            if (j==0) {
+                t1=t1+wheel.getPulses();
+                wheel.reset();
+                if (t1>5*60) { // Tiempos no mayores a 6 minutos
+                    t1=5*60;
+                }
+                if (t1<60) {
+                    t1=60;
+                }
+                lcd.locate(3,0);
+                lcd.printf("   ",t1);
+                lcd.locate(3,0);
+                lcd.printf("%d",t1);
+                wait(0.2);
+
+                if(button3.falling()) {
+                    j=1;
+                    led3=0;
+                    wait(0.3);
+                    wheel.reset();
+                }
+            }
+
+            if (j==1) {
+                t2=t2+wheel.getPulses();
+                wheel.reset();
+                if (t2>5*60) {
+                    t2=5*60;
+                }
+                if (t2<60) {
+                    t2=60;
+                }
+                lcd.locate(11,0);
+                lcd.printf("   ");
+                lcd.locate(11,0);
+                lcd.printf("%d",t2);
+                wait(0.2);
+
+                if(button3.falling()) {
+                    j=2;
+                    led3=0;
+                    wait(0.3);
+                    wheel.reset();
+                }
+            }
+            if (j==2) {
+                t3=t3+wheel.getPulses();
+                wheel.reset();
+                if (t3>5*60) {
+                    t3=5*60;
+                }
+                if (t3<60) {
+                    t3=60;
+                }
+                lcd.locate(3,1);
+                lcd.printf("     ");
+                lcd.locate(3,1);
+                lcd.printf("%d",t3);
+                wait(0.2);
+
+                if(button3.falling()) {
+                    j=3;
+                    led3=0;
+                    wait(0.3);
+                    wheel.reset();
+                }
+            }
+            if (j==3) {
+                temp=temp+wheel.getPulses();
+                wheel.reset();
+                if (temp>60) {
+                    temp=60;
+                }
+                if (temp<20) {
+                    temp=20;
+                }
+                lcd.locate(11,1);
+                lcd.printf("     ");
+                lcd.locate(11,1);
+                lcd.printf("%d",temp);
+                wait(0.2);
+
+                if(button3.falling()) {
+                    j=0;
+                    led3=0;
+                    wait(0.3);
+                    wheel.reset();
+                }
+            }
+            if (j==4) {
+                j=0;
+            }
+            if (button4.falling()) {
+                break;
+            }
+        }//CIERRE MODO i=2 -> tiempo y temperatura
+    }// Cierre del while(1)
+//%---------------------------------------------------------------------
+
+
+    lcd.writeCommand(C4);//escribimos un comando segun el manual del modulo LCD para quitar cursor bajo
+    lcd.cls(); //borra la pantalla
+    lcd.printf("Valores \nGuardados");
+    wait(1);
+
+    avg=0;
+    for(i=0; i<10; i++) {
+        a[i]=LM.read();
+        wait(.001);
+    }
+    for(i=0; i<10; i++) {
+        avg=avg+(a[i]/10);
+    }
+
+    ta=(avg*3.10143686*100);
+
+    lcd.cls(); //borra la pantalla
+    lcd.printf("T Ambiente:%.2f",ta);
+    wait(2);
+
+    // se imprimen los parches del control  *****************************************
+    lcd.cls();
+    lcd.printf("Er:%.2f",err);
+    lcd.locate(8,0);
+    lcd.printf("Me:%.2f",med);
+    lcd.locate(0,1);
+    lcd.printf("dt:%.2f",dt1);
+    lcd.locate(10,1);
+    lcd.printf("t:%.0f",0);
+    wait(1);
+
+    // CICLO PARA CONTROL DE RAMPA ASCENDENTE (T1)
+    b=0;
+    t.start();
+    // Comienza el ciclo for para la primera parte del control(Rampa ascendente)
+    for(dt1=ta; dt1<=temp; dt1+=(temp-ta)/t1) {
+        avg=0;
+        for(i=0; i<10; i++) {
+            a[i]=LM.read();
+            wait(.001);
         }
-        
\ No newline at end of file
+        for(i=0; i<10; i++) {
+            avg=avg+(a[i]/10);
+        }
+        med=(avg*3.10143686*100); //leer puerto analogo y asignar a med (*100 ya que LM35 da 10mV/ºC)
+        err = (dt1-med);
+        // Para activar la bombilla en el tiempo de ascenso
+        if(err>=0) {
+            vent=0;
+            ap = kp*err;   //calculo de la acción proporcional
+            // se verifica que la accion integral no sea muy grande
+            if(ai<100) {
+                ai =(ki*err)+ai;    //calculo de la integral del error
+            }
+            ad = kd*(err-err_v); //calculo de la accion derivativa
+            pid = (ap+ai+ad);
+            // se verifica que pid sea positivo **************************************
+            if(pid<=0) {
+                pid=0;
+            }
+            // se verifica que pid sea menor o igual la valor maximo *****************
+            if(pid>=100) {
+                pid=100;
+            }
+            err_v = err;
+            //Normalizacion de la salida
+            pidn=pid/100;
+            //  se envia el valor pid a puerto analogico de salida (D/A) **************
+            bomb.write(pidn);
+        }
+        // Para activar ventilador en el tiempo de ascenso
+        if(err<0) {
+            bomb=0;
+            err=-1*err;
+            ap = kp*err;
+            if(ai<100) {
+                ai =(ki*err)+ai;
+            }
+            ad = kd*(err-err_v);
+            pid = (ap+ai+ad);
+            if(pid<=0) {
+                pid=0;
+            }
+            if(pid>=100) {
+                pid=100;
+            }
+            err_v = err;
+            pidn=pid/100;
+            vent.write(pidn);
+        }
+        //  se repite el ciclo
+        lcd.cls();
+        lcd.locate(0,0);
+        lcd.printf("Er:%.2f",err);
+        lcd.locate(8,0);
+        lcd.printf("Me:%.2f",med);
+        lcd.locate(0,1);
+        lcd.printf("dt:%.2f",dt1);
+        lcd.locate(10,1);
+        lcd.printf("t1:%.0f",t.read());
+        wait(1);
+    }
+    bomb=0;
+    vent=0;
+    // Comienza el segundo ciclo para la segunda parte del control (Temperatura estable)
+    t.start();
+    while(t<=t2) {
+        avg=0;
+        for(i=0; i<10; i++) {
+            a[i]=LM.read();
+            wait(.001);
+        }
+        for(i=0; i<10; i++) {
+            avg=avg+(a[i]/10);
+        }
+
+        med=(avg*3.10143686*100);  //leer puerto analogo y asignar a med (*100 ya que LM35 da 10mV/ºC)
+        err = (temp-med);
+        // Para activar la bombilla en el tiempo de ascenso
+        if(err>=0) {
+            vent=0;
+            ap = kp*err;   //calculo de la acción proporcional
+            // se verifica que la accion integral no sea muy grande
+            if(ai<100) {
+                ai =(ki*err)+ai;    //calculo de la integral del error
+            }
+            ad = kd*(err-err_v); //calculo de la accion derivativa
+            pid = (ap+ai+ad);
+            // se verifica que pid sea positivo **************************************
+            if(pid<=0) {
+                pid=0;
+            }
+            // se verifica que pid sea menor o igual la valor maximo *****************
+            if(pid>=100) {
+                pid=100;
+            }
+            err_v = err;
+            //Normalizacion de la salida
+            pidn=pid/100;
+            //  se envia el valor pid a puerto analogico de salida (D/A) **************
+            bomb.write(pidn);
+        }
+        // Para activar ventilador en el tiempo de ascenso
+        if(err<0) {
+            bomb=0;
+            err=-1*err;
+            ap = kp*err;
+            if(ai<100) {
+                ai =(ki*err)+ai;
+            }
+            ad = kd*(err-err_v);
+            pid = (ap+ai+ad);
+            if(pid<=0) {
+                pid=0;
+            }
+            if(pid>=100) {
+                pid=100;
+            }
+            err_v = err;
+            //Normalizacion de la salida
+            pidn=pid/100;
+            //  se envia el valor pid a puerto analogico de salida (D/A) **************
+            vent.write(pidn);
+        }
+        lcd.cls();
+        lcd.locate(0,0);
+        lcd.printf("Er:%.2f",err);
+        lcd.locate(8,0);
+        lcd.printf("Me:%.2f",med);
+        lcd.locate(0,1);
+        lcd.printf("Tm:%.2d",temp);
+        lcd.locate(10,1);
+        lcd.printf("t2:%.0f",t.read());
+        wait(1);
+    }
+    bomb=0;
+    vent=0;
+    // Comienza el tercer ciclo para la ultima parte del control (Descenso de rampa)
+    t.start();
+    for(dt3=temp; dt3>=ta; dt3-=((temp-ta)/t3)) {
+        avg=0;
+        for(i=0; i<10; i++) {
+            a[i]=LM.read();
+            wait(.001);
+        }
+        for(i=0; i<10; i++) {
+            avg=avg+(a[i]/10);
+        }
+
+        med=(avg*3.10143686*100);//leer puerto analogo y asignar a med (*100 ya que LM35 da 10mV/ºC)
+        err = (med-dt3);
+        if(err>=0) {
+            bomb=0;
+            ap = kp*err;   //calculo de la acción proporcional
+            // se verifica que la accion integral no sea muy grande
+            if(ai<100) {
+                ai =(ki*err)+ai;    //calculo de la integral del error
+            }
+            ad = kd*(err-err_v); //calculo de la accion derivativa
+            pid = (ap+ai+ad);
+            // se verifica que pid sea positivo **************************************
+            if(pid<=0) {
+                pid=0;
+            }
+            // se verifica que pid sea menor o igual la valor maximo *****************
+            if(pid>=100) {
+                pid=100;
+            }
+            // se actualizan las variables *******************************************
+            err_v = err;
+            lcd.cls();
+            lcd.locate(0,0);
+            lcd.printf("Er:%.2f",err);
+            lcd.locate(8,0);
+            lcd.printf("Me:%.2f",med);
+            lcd.locate(0,1);
+            lcd.printf("dt:%.2f",dt3);
+            lcd.locate(10,1);
+            lcd.printf("t3:%.0f",t.read());
+
+            //Normalizacion de la salida
+            pidn=pid/100;
+            //  se envia el valor pid a puerto analogico de salida (D/A) **************
+            vent.write(pidn);
+            //  se repite el ciclo
+            wait(1);
+        }
+        if(err<0) {
+            err=-1*err;
+            vent=0;
+            ap = kp*err;   //calculo de la acción proporcional
+            // se verifica que la accion integral no sea muy grande
+            if(ai<100) {
+                ai =(ki*err)+ai;    //calculo de la integral del error
+            }
+            ad = kd*(err-err_v); //calculo de la accion derivativa
+            pid = (ap+ai+ad);
+            // se verifica que pid sea positivo **************************************
+            if(pid<=0) {
+                pid=0;
+            }
+            // se verifica que pid sea menor o igual la valor maximo *****************
+            if(pid>=100) {
+                pid=100;
+            }
+            // se actualizan las variables *******************************************
+            err_v = err;
+            lcd.cls();
+            lcd.locate(0,0);
+            lcd.printf("Er:%.2f",err);
+            lcd.locate(8,0);
+            lcd.printf("Me:%.2f",med);
+            lcd.locate(0,1);
+            lcd.printf("dt:%.2f",dt3);
+            lcd.locate(10,1);
+            lcd.printf("t3:%.0f",t.read());
+
+            //Normalizacion de la salida
+            pidn=pid/100;
+            //  se envia el valor pid a puerto analogico de salida (D/A) **************
+            bomb.write(pidn);
+            //  se repite el ciclo
+            wait(1);
+        }
+
+    }
+    bomb=0;
+    vent=0;
+    lcd.cls();
+    lcd.printf("Control\nTreminado");
+}
diff -r d42fe3777735 -r ad88703dc6d3 mbed.bld
--- a/mbed.bld	Sat May 24 15:05:40 2014 +0000
+++ b/mbed.bld	Thu Jun 12 16:43:28 2014 +0000
@@ -1,1 +1,1 @@
-http://mbed.org/users/mbed_official/code/mbed/builds/a9913a65894f
\ No newline at end of file
+http://mbed.org/users/mbed_official/code/mbed/builds/0b3ab51c8877
\ No newline at end of file