joe di castrohttp://joedicastro.com2012-06-12T10:44:00+02:00Mantener la temperatura adecuada en un portátil Dell con Linux2012-06-12T10:44:00+02:00joe di castrohttp://joedicastro.com/mantener-la-temperatura-adecuada-en-un-portatil-dell-con-linux.html<p>Los que tengáis o hayáis tenido un portátil <strong>Dell</strong>, sabréis que es posible controlar la velocidad de funcionamiento de sus ventiladores de forma manual. Existen aplicaciones para Windows, pero también es posible hacerlo desde Linux con el paquete <code>i8kutils</code> creado por Massimo Dal Zotto. Este paquete incluye un modulo del kernel <code>i8k</code> que necesita ser cargado al inicio y una serie de utilidades para controlar el ventilador e informar de la temperatura y otros valores de la <a href="https://es.wikipedia.org/wiki/Bios">BIOS</a>.</p> <p>Este paquete fue creado originalmente para el portátil Dell Inspiron 8000, de ahí su nombre. Esta utilidad aprovecha el modo <a href="http://es.wikipedia.org/wiki/Modo_de_Gerencia_del_Sistema">SMM </a> de la BIOS que estaba presente en los modelos Inspiron para controlar la velocidad de los ventiladores (algo que también permiten otras marcas como Toshiba y Lenovo, para las que también hay algunas utilidades). Dell también ofrece el soporte de SMM BIOS en otras gamas y portátiles, como los de la serie Latitude o XPS. Aunque está utilidad no funciona en algunos modelos.</p> <p>Esta utilidad funciona bajo la línea de comandos, pero al amparo del modulo del kernel surgieron varias aplicaciones, principalmente applets para el panel de Gnome 2, que permitían un control gráfico de los ventiladores y su temperatura. Este control podía ser automático o manual. Yo personalmente me decanté en su momento por emplear <a href="http://conky.sourceforge.net/">Conky</a> para mostrar las velocidades y la temperatura y emplear un script, <code>i8kapplet</code> por Wheelspin, para controlar automáticamente la temperatura dentro de unos rangos.</p> <h2 id="i8kfanspy">i8kfans.py</h2> <p>Este era un script bash que lleva dándome servicio mucho años (el portátil ya tiene sus siete años) pero que por la forma que tiene de controlar cuando se debe subir/bajar la velocidad de los ventiladores, provocaba que se sucediesen de vez en cuando continuos acelerones y frenazos en los mismos. Esto al principio no me disgustaba, pero con los años los ventiladores hacen cada vez más ruido y si bien el sonido constante a alta velocidad es ligeramente molesto, esos cambios bruscos de velocidad se me han vuelto insoportables. Y dado que el portátil se acerca al final de su vida útil, sustituir los ventiladores, aunque es la solución adecuada, no lo veo económicamente rentable.</p> <p>Yo sabía que el problema por el que esto sucedía es porque la BIOS por defecto regula las temperaturas de los ventiladores en unos rangos predefinidos y esto no se desactiva, de hecho trabaja conjuntamente con el script. Como los rangos de temperatura que yo predefino son inferiores a los de la BIOS, en algunas ocasiones los dos pelean por el control de los ventiladores y es lo que ocasiona el problema. Pero el calor es el peor enemigo de la electrónica, y en los portátiles esto es un factor critico. De hecho estoy seguro de que este equipo (muy bien amortizado) me ha durado tantos años gracias a que me he preocupado de este punto. Estoy cansado de ver morir a portátiles y discos duros en verano porque la gente no se preocupa de este tema. Por favor, limpiad el polvo de los ventiladores al llegar el verano, os ahorrareis muchos disgustos.</p> <p>Así que descartado el reemplazar los ventiladores, me planteé el crear un script que intentara hacer lo mismo pero de forma más suave, intentado reducir el número de cambios bruscos de velocidad y fruto de ello es el siguiente script, que está disponible en <a href="http://github.com/joedicastro/i8kfans">GitHub</a>:</p> <div class="codehilite"><pre><span class="c">#!/usr/bin/env python</span> <span class="c"># -*- coding: utf8 -*-</span> <span class="sd">&quot;&quot;&quot;</span> <span class="sd"> i8kfans.py: Adjust the fans speed in various Dell laptops (with a nvidia</span> <span class="sd"> graphics card) to maintain the right temperatures. This affect both fans,</span> <span class="sd"> the cpu and the gpu fan. Originally i8k was created to run in a Dell</span> <span class="sd"> Inspiron 8000 laptop, but this Dell fan control via SMM BIOS is available</span> <span class="sd"> in others laptops of various series (Inspiron, XPS, Latitude, etcetera),</span> <span class="sd"> but not all of them are supported. Mine is an Inspiron 9400 but I tested</span> <span class="sd"> this successfully in a XPS m1330 too.</span> <span class="sd"> Based on a 2006 bash script by Wheelspin, `i8kapplet`. This old script</span> <span class="sd"> served faithfully me for many years, but my ears couldn&#39;t stand much longer</span> <span class="sd"> its random and common slow downs/speed ups. Over the years, fans have</span> <span class="sd"> become more and more loud. This new script runs in a more smooth way, with</span> <span class="sd"> less sudden changes. It&#39;s cheaper than replace booth fans, don&#39;t you</span> <span class="sd"> think?</span> <span class="sd"> This script needs the `i8kutils` linux package installed and the `i8k`</span> <span class="sd"> kernel module loaded to work.</span> <span class="sd">&quot;&quot;&quot;</span> <span class="c">#==============================================================================</span> <span class="c"># Copyright 2012 joe di castro &lt;joe@joedicastro.com&gt;</span> <span class="c">#</span> <span class="c"># This program is free software: you can redistribute it and/or modify</span> <span class="c"># it under the terms of the GNU General Public License as published by</span> <span class="c"># the Free Software Foundation, either version 3 of the License, or</span> <span class="c"># (at your option) any later version.</span> <span class="c">#</span> <span class="c"># This program is distributed in the hope that it will be useful,</span> <span class="c"># but WITHOUT ANY WARRANTY; without even the implied warranty of</span> <span class="c"># MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the</span> <span class="c"># GNU General Public License for more details.</span> <span class="c">#</span> <span class="c"># You should have received a copy of the GNU General Public License</span> <span class="c"># along with this program. If not, see &lt;http://www.gnu.org/licenses/&gt;.</span> <span class="c">#==============================================================================</span> <span class="n">__author__</span> <span class="o">=</span> <span class="s">&quot;joe di castro &lt;joe@joedicastro.com&gt;&quot;</span> <span class="n">__license__</span> <span class="o">=</span> <span class="s">&quot;GNU General Public License version 3&quot;</span> <span class="n">__date__</span> <span class="o">=</span> <span class="s">&quot;12/06/2012&quot;</span> <span class="n">__version__</span> <span class="o">=</span> <span class="s">&quot;0.3&quot;</span> <span class="k">try</span><span class="p">:</span> <span class="kn">from</span> <span class="nn">os</span> <span class="kn">import</span> <span class="n">linesep</span> <span class="kn">from</span> <span class="nn">subprocess</span> <span class="kn">import</span> <span class="n">check_output</span><span class="p">,</span> <span class="n">Popen</span><span class="p">,</span> <span class="n">PIPE</span> <span class="kn">from</span> <span class="nn">sys</span> <span class="kn">import</span> <span class="nb">exit</span><span class="p">,</span> <span class="n">exc_info</span> <span class="kn">from</span> <span class="nn">time</span> <span class="kn">import</span> <span class="n">sleep</span> <span class="k">except</span> <span class="ne">ImportError</span><span class="p">:</span> <span class="c"># Checks the installation of the necessary python modules</span> <span class="k">print</span><span class="p">((</span><span class="n">linesep</span> <span class="o">*</span> <span class="mi">2</span><span class="p">)</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="s">&quot;An error found importing one module:&quot;</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">exc_info</span><span class="p">()[</span><span class="mi">1</span><span class="p">]),</span> <span class="s">&quot;You need to install it&quot;</span><span class="p">,</span> <span class="s">&quot;Stopping...&quot;</span><span class="p">]))</span> <span class="nb">exit</span><span class="p">(</span><span class="o">-</span><span class="mi">2</span><span class="p">)</span> <span class="k">def</span> <span class="nf">check_execs</span><span class="p">(</span><span class="o">*</span><span class="n">progs</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot;Check if the programs are installed, if not exit and report.&quot;&quot;&quot;</span> <span class="k">for</span> <span class="n">prog</span> <span class="ow">in</span> <span class="n">progs</span><span class="p">:</span> <span class="k">try</span><span class="p">:</span> <span class="n">Popen</span><span class="p">([</span><span class="n">prog</span><span class="p">,</span> <span class="s">&#39;--help&#39;</span><span class="p">],</span> <span class="n">stdout</span><span class="o">=</span><span class="n">PIPE</span><span class="p">,</span> <span class="n">stderr</span><span class="o">=</span><span class="n">PIPE</span><span class="p">)</span> <span class="k">except</span> <span class="ne">OSError</span><span class="p">:</span> <span class="n">msg</span> <span class="o">=</span> <span class="s">&#39;The {0} program is necessary to run this script&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">prog</span><span class="p">)</span> <span class="nb">exit</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span> <span class="k">return</span> <span class="k">def</span> <span class="nf">get_right_fan_speed</span><span class="p">(</span><span class="n">current_temperature</span><span class="p">,</span> <span class="n">current_fan_speed</span><span class="p">,</span> <span class="n">temp_triggers</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot;Get the right fan speed to use with i8kfan command.</span> <span class="sd"> :current_temperature: current temperature value for the fan implied</span> <span class="sd"> :current_fan_speed: current fan speed</span> <span class="sd"> :temp_triggers: the threshold temperatures to trigger the fan speed change</span> <span class="sd"> :returns: right fan speed or &quot;-&quot; (means change nothing to i8kfan)</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="n">right_fan_speed</span> <span class="o">=</span> <span class="bp">None</span> <span class="c"># the right fan speed for the current temp</span> <span class="k">if</span> <span class="n">current_temperature</span> <span class="o">&gt;=</span> <span class="n">temp_triggers</span><span class="p">[</span><span class="mi">0</span><span class="p">]:</span> <span class="k">if</span> <span class="n">current_temperature</span> <span class="o">&gt;=</span> <span class="n">temp_triggers</span><span class="p">[</span><span class="mi">1</span><span class="p">]:</span> <span class="n">right_fan_speed</span> <span class="o">=</span> <span class="mi">2</span> <span class="k">else</span><span class="p">:</span> <span class="n">right_fan_speed</span> <span class="o">=</span> <span class="mi">1</span> <span class="k">else</span><span class="p">:</span> <span class="n">right_fan_speed</span> <span class="o">=</span> <span class="mi">0</span> <span class="k">return</span> <span class="n">right_fan_speed</span> <span class="k">if</span> <span class="n">right_fan_speed</span> <span class="o">!=</span> <span class="n">current_fan_speed</span> <span class="k">else</span> <span class="s">&quot;-&quot;</span> <span class="k">def</span> <span class="nf">main</span><span class="p">():</span> <span class="sd">&quot;&quot;&quot;Main section&quot;&quot;&quot;</span> <span class="c"># time between temperature checks</span> <span class="n">interval</span> <span class="o">=</span> <span class="mi">1</span> <span class="c"># the temp thresholds to jump to a faster fan speed. Values greater than</span> <span class="c"># [g|c]pu[0] set the fan speed to 1 and the ones greater than [g|c]pu[1]</span> <span class="c"># set the speed to 2. Obviously, values minor than [g|c]pu[0] stop the fan</span> <span class="n">gpu_temps</span> <span class="o">=</span> <span class="p">[</span><span class="mi">45</span><span class="p">,</span> <span class="mi">53</span><span class="p">]</span> <span class="n">cpu_temps</span> <span class="o">=</span> <span class="p">[</span><span class="mi">40</span><span class="p">,</span> <span class="mi">50</span><span class="p">]</span> <span class="c"># check if the i8k kernel module is already loaded</span> <span class="k">if</span> <span class="s">&quot;i8k&quot;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">check_output</span><span class="p">(</span><span class="s">&quot;ls /proc/&quot;</span><span class="o">.</span><span class="n">split</span><span class="p">()):</span> <span class="nb">exit</span><span class="p">(</span><span class="s">&quot;The i8k kernel module is not loaded&quot;</span><span class="p">)</span> <span class="k">while</span> <span class="bp">True</span><span class="p">:</span> <span class="k">try</span><span class="p">:</span> <span class="c"># get current values</span> <span class="n">cpu_temp</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">check_output</span><span class="p">(</span><span class="s">&quot;i8kctl temp&quot;</span><span class="o">.</span><span class="n">split</span><span class="p">()))</span> <span class="n">gpu_out</span> <span class="o">=</span> <span class="n">check_output</span><span class="p">(</span><span class="s">&quot;nvidia-smi -q -d TEMPERATURE&quot;</span><span class="o">.</span><span class="n">split</span><span class="p">())</span> <span class="n">gpu_temp</span> <span class="o">=</span> <span class="nb">int</span><span class="p">([</span><span class="n">s</span> <span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">gpu_out</span><span class="o">.</span><span class="n">split</span><span class="p">()</span> <span class="k">if</span> <span class="n">s</span><span class="o">.</span><span class="n">isdigit</span><span class="p">()][</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span> <span class="n">cpu_fan</span><span class="p">,</span> <span class="n">gpu_fan</span> <span class="o">=</span> <span class="p">[</span><span class="nb">int</span><span class="p">(</span><span class="n">f</span><span class="p">)</span> <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">check_output</span><span class="p">(</span><span class="s">&quot;i8kfan&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">split</span><span class="p">()]</span> <span class="c"># get the right speed values for each fan</span> <span class="n">cpu_rfs</span> <span class="o">=</span> <span class="n">get_right_fan_speed</span><span class="p">(</span><span class="n">cpu_temp</span><span class="p">,</span> <span class="n">cpu_fan</span><span class="p">,</span> <span class="n">cpu_temps</span><span class="p">)</span> <span class="n">gpu_rfs</span> <span class="o">=</span> <span class="n">get_right_fan_speed</span><span class="p">(</span><span class="n">gpu_temp</span><span class="p">,</span> <span class="n">gpu_fan</span><span class="p">,</span> <span class="n">gpu_temps</span><span class="p">)</span> <span class="c"># if any of the fans needs to change their speed, change it!</span> <span class="k">if</span> <span class="n">cpu_rfs</span> <span class="o">!=</span> <span class="s">&quot;-&quot;</span> <span class="ow">or</span> <span class="n">gpu_rfs</span> <span class="o">!=</span> <span class="s">&quot;-&quot;</span><span class="p">:</span> <span class="n">Popen</span><span class="p">(</span><span class="s">&quot;i8kfan {0} {1}&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">cpu_rfs</span><span class="p">,</span> <span class="n">gpu_rfs</span><span class="p">)</span><span class="o">.</span><span class="n">split</span><span class="p">(),</span> <span class="n">stdout</span><span class="o">=</span><span class="n">PIPE</span><span class="p">)</span> <span class="c"># wait a moment. We want a cooler laptop, aren&#39;t we?</span> <span class="n">sleep</span><span class="p">(</span><span class="n">interval</span><span class="p">)</span> <span class="k">except</span> <span class="ne">KeyboardInterrupt</span><span class="p">:</span> <span class="nb">exit</span><span class="p">()</span> <span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">&quot;__main__&quot;</span><span class="p">:</span> <span class="n">check_execs</span><span class="p">(</span><span class="s">&quot;i8kctl&quot;</span><span class="p">,</span> <span class="s">&quot;i8kfan&quot;</span><span class="p">,</span> <span class="s">&quot;nvidia-smi&quot;</span><span class="p">)</span> <span class="n">main</span><span class="p">()</span> <span class="c">###############################################################################</span> <span class="c"># Changelog #</span> <span class="c">###############################################################################</span> <span class="c">#</span> <span class="c"># 0.3:</span> <span class="c">#</span> <span class="c"># * Better documentation</span> <span class="c">#</span> <span class="c"># 0.2:</span> <span class="c">#</span> <span class="c"># * Fix an error in a function docstring due to refactorization</span> <span class="c"># * Give appropriate credit to original idea&#39; script</span> <span class="c">#</span> <span class="c"># 0.1:</span> <span class="c">#</span> <span class="c"># * First attempt</span> <span class="c">#</span> </pre></div> <p>De momento, aunque aún sufre del mismo problema inevitable por el conflicto por el control entre el script y la BIOS, de momento he observado que se produce con menos frecuencia y durante menos tiempo. Aunque quizás realice algunos cambios para intentar reducir aún más esos conflictos.</p>