Analog-Digital-Analoges Thermometer

Ich habe mir ein analoges Voltmeter zugelegt und möchte es als Thermometer benutzen.

Da der Widerstand von Metallen mit der Temperatur steigt, kann man Temperatur relativ gut messen, indem man einen kalibrierten Widerstand misst. Daher kann man theoretisch mit einem Multimeter auch die Temperatur messen. (In der Praxis wird dies bei Multimetern allerdings in der Regel mithilfe eines anderen Effektes erledigt.)

Da ich mir aber keine Gedanken darüber machen möchte, wie ich eine Schaltung aussehen müsste, um \(15°\mathrm{C}\) in \(1.5 \mathrm{V}\) umzusetzen (vielleicht würde eine Brückenschaltung funktionieren?), wähle ich den einfachen Weg mit einer Reihe integrierter Schaltkreise und einem Microcontroller.

Schaltplan meines Analog-Digital-Analog-Thermometers

Hier ist ein günstiger DS18B20 Temperatursensor, der von einem ESP8266 ausgelesen wird. Dieser steuert dann einen MCP4725 Digital-Analog-Wandler so an, dass er eine Spannung ausgibt, deren Wert in Volt ein Zehntel der gemessenen Temperatur ist. Diese Spannung wird dann von meinem alten Voltmeter gemessen und angezeigt. Hier ist es also gerade \(24°\mathrm{C}\).

Foto meines Analog-Digital-Analog-Thermometers

Hier ist übrigens der simple Code, der beispielsweise mit der Arduino IDE auf einen ESP8266 geflasht werden kann:

#include <Wire.h>
#include <Adafruit_MCP4725.h>
#include <OneWire.h>
#include <DallasTemperature.h>

#define ONE_WIRE_BUS D4
#define MCP4725In A0

Adafruit_MCP4725 MCP4725;

OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature DS18B20(&oneWire);

void setup() {
    Serial.begin(9600);

    DS18B20.begin();
    // 0x60 is the I2C address of my MCP4725A0
    MCP4725.begin(0x60);
}

float getTemperature() {
    float temp;
    do {
      DS18B20.requestTemperatures();
      temp = DS18B20.getTempCByIndex(0);
      delay(100);
    } while (temp == 85.0 || temp == (-127.0));
    return temp;
}

void setVoltage(float value) {
    value /= 10;
    float voltageOut = value*4096/3.3;
    MCP4725.setVoltage(voltageOut, false);

    // read it for testing and maybe calibrating
    int adcInput = analogRead(MCP4725In);
    float voltageIn = (adcInput * 3.3 )/ 1024.0;
    Serial.print("Expected Voltage: ");
    Serial.println(value, 3);

    Serial.print("Measured Voltage: ");
    Serial.println(voltageIn, 3);
}

void loop() {

    float temperature = getTemperature();
    setVoltage(temperature);

    // send temperature to the serial console
    dtostrf(temperature, 2, 2, temperatureString);
    Serial.println(temperatureString);

    delay(1e3);
}

Pebble Rules

Im letzten Monat habe ich jemanden getroffen, auf dessen Armbanduhr eine MCMC Simulation von Hamilton-Pfaden auf einem quadratischen Gitter liefen. Ich war derartig begeistert, dass ich beschlossen habe auch etwas auf meiner Pebble simulieren zu lassen. Aufgrund der geringen Auflösung des Displays (\(144 \times 168\)) bieten sich „blockige“ Visualisierungen an. Glücklicherweise habe ich schon genügend Spielereien geschrieben, die sich eignen [1, 2, 3, 4].

Pebble wurde zwar inzwischen von Fitbit aufgekauft, aber das SDK ist noch verfügbar. Die neueren Exemplare lassen sich per JavaScript programmieren, meine „Kickstarter Edition“ aus der ersten Generation allerdings noch nicht.

Da ich meine Uhr also in C programmieren muss, konnte ich allerdings den den alten Code aus Wolfram’s Rules wiederbenutzen.

Der Code ist auf GitHub verfügbar.

Perfect Dependencies

Man hat ein großes C++ Projekt, ändert einen Header, führt make aus und ein seltsamer Fehler tritt im Programm auf. Das liegt natürlich daran, dass make nicht alle Quelldateien neu kompiliert hat, die den Header einbinden. Woher sollte make das auch wissen? Alle Header per Hand in der Makefile einzutragen und zu pflegen, ist Wahnsinn und wird den Programmierer in denselben treiben.

make ist ein sehr allgemein gehaltenes Programm, wie ich in einem vorherigen Eintrag gezeigt habe. Sich um Eigenheiten von C oder C++ zu kümmern fällt also nicht in den Aufgabenbereich von make. Aber glücklicherweise gibt es ein Programm, dessen Hauptaufgabe es ist, sich mit den Eigenheiten von C bzw. C++ auszukennen: den Compiler. Tatsächlich bietet (zumindest GCC) die Option eine C oder C++ Datei zu parsen und alle inkludierten Header auszugeben.

g++ -MM myCode.cpp

Das ausnutzend, bietet die offizielle Dokumentation von GNU make folgende Rule, um je eine „dependency“ Makefile pro .c Datei zu erzeugen und automatisch einzubinden.

%.d: %.c
    @set -e; rm -f $@; \
    $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
    sed ’s,\($*\)\.o[ :]*,\1.o $@ : ,g’ < $@.$$$$ > $@; \
    rm -f $@.$$$$

include $(sources:.c=.d)

Falls man .o Dateien in ein obj/ Verzeichnis speichert, muss man die Regex anpassen. In meinen Projekten leistet mir diese Rule gute Dienste.

Alternativ könnte man natürlich auf ein anderes Buildsystem statt handgepflegter Makefiles umsteigen.