Arnov Sharma
Published © MIT

TempGun Pro

TempGun Pro is an open-source temperature gun powered by a custom ESP32 C6 devboard paired with an MLX90614 infrared temperature sensor.

BeginnerFull instructions provided6 hours143
TempGun Pro

Things used in this project

Hardware components

PCBWay Custom PCB
PCBWay Custom PCB
×1

Software apps and online services

Fusion
Autodesk Fusion
Arduino IDE
Arduino IDE

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)

Story

Read more

Custom parts and enclosures

cad file

Schematics

SCH

Code

CODE

C/C++
#include <Arduino_GFX_Library.h>
#include <Wire.h>
#include <Adafruit_MLX90614.h>

// --- Pin Definitions for WAVESHARE ESP32-C6 LCD BOARD ---
#define LCD_MOSI 6
#define LCD_SCLK 7
#define LCD_CS   14
#define LCD_DC   15
#define LCD_RST  21
#define LCD_BL   22

// I2C pins for MLX90614 (as you confirmed they work)
#define SDA_PIN 4
#define SCL_PIN 5

// Button pin
#define TRIGGER_PIN 18

// --- Global Objects ---

// Create an MLX90614 object
Adafruit_MLX90614 mlx = Adafruit_MLX90614();

// Create the display objects
Arduino_DataBus *bus = new Arduino_ESP32SPI(
  LCD_DC, LCD_CS, LCD_SCLK, LCD_MOSI, GFX_NOT_DEFINED
);
Arduino_GFX *gfx = new Arduino_ST7789(
  bus, LCD_RST, 2, true, 172, 320, 34, 0, 34, 0
);

// --- State and Timing Variables ---

enum GunState {
  READY,
  MEASURING,
  DISPLAY_RESULT
};

GunState currentState = READY;
unsigned long lastMeasurementTime = 0;
const unsigned long resultDisplayTime = 5000; // Display result for 5 seconds

// Button debouncing
bool lastButtonState = HIGH;
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 50;

// --- Drawing Functions ---

void drawReadyScreen() {
  gfx->fillScreen(BLACK);

  // Draw the crosshair
  int centerX = gfx->width() / 2;   // Center of 320px width
  int centerY = gfx->height() / 2;  // Center of 172px height
  gfx->drawCircle(centerX, centerY, 30, WHITE);
  gfx->drawFastHLine(centerX - 40, centerY, 80, WHITE);
  gfx->drawFastVLine(centerX, centerY - 40, 80, WHITE);
  gfx->drawCircle(centerX, centerY, 5, RED);
  gfx->fillCircle(centerX, centerY, 2, RED);

  // --- Draw "TempGun Pro" title ---
  int16_t x1, y1;
  uint16_t w, h;

  // Draw "TempGun"
  gfx->setTextSize(4);
  gfx->setTextColor(RED);
  gfx->getTextBounds("TempGun", 0, 0, &x1, &y1, &w, &h);
  int tempgun_y = 35;
  gfx->setCursor((gfx->width() - w) / 2, tempgun_y);
  gfx->print("TempGun");

  // Draw "PRO" below it, slightly bigger
  gfx->setTextSize(5); // Slightly bigger
  gfx->getTextBounds("PRO", 0, 0, &x1, &y1, &w, &h);
  int pro_y = tempgun_y + h + 2; // Position below "TempGun"
  gfx->setCursor((gfx->width() - w) / 2, pro_y);
  gfx->println("PRO");

  // --- Draw instruction text ---
  gfx->setTextSize(1);
  gfx->setTextColor(WHITE);
  gfx->getTextBounds("Press button to measure", 0, 0, &x1, &y1, &w, &h);
  gfx->setCursor((gfx->width() - w) / 2, gfx->height() - 15);
  gfx->println("Press button to measure");
}

void drawMeasuringScreen() {
  // Redraw crosshair in red to indicate scanning
  int centerX = gfx->width() / 2;
  int centerY = gfx->height() / 2;
  gfx->drawCircle(centerX, centerY, 30, RED);
  gfx->drawFastHLine(centerX - 40, centerY, 80, RED);
  gfx->drawFastVLine(centerX, centerY - 40, 80, RED);

  // Clear the old text and write new text
  int16_t x1, y1;
  uint16_t w, h;
  gfx->setTextSize(1);
  gfx->setTextColor(ORANGE);
  gfx->getTextBounds("SCANNING...", 0, 0, &x1, &y1, &w, &h);
  gfx->fillRect(0, gfx->height() - 15, gfx->width(), h + 5, BLACK);
  gfx->setCursor((gfx->width() - w) / 2, gfx->height() - 15);
  gfx->println("SCANNING...");
}

void drawResultScreen(float objTemp, float ambTemp) {
  gfx->fillScreen(BLACK);

  // --- Draw "TempGun Pro" title ---
  int16_t x1, y1;
  uint16_t w, h;

  // Draw "TempGun"
  gfx->setTextSize(3);
  gfx->setTextColor(ORANGE);
  gfx->getTextBounds("TempGun", 0, 0, &x1, &y1, &w, &h);
  int tempgun_y = 25;
  gfx->setCursor((gfx->width() - w) / 2, tempgun_y);
  gfx->print("TempGun");

  // Draw "PRO" below it, slightly bigger
  gfx->setTextSize(4); // Slightly bigger
  gfx->getTextBounds("PRO", 0, 0, &x1, &y1, &w, &h);
  int pro_y = tempgun_y + h + 2; // Position below "TempGun"
  gfx->setCursor((gfx->width() - w) / 2, pro_y);
  gfx->println("PRO");

  // --- Adjusted Temperature Values Layout ---
  int label_y = 100; // Shifted down from 80
  int value_y_c = label_y + 15; // Position for Celsius value
  int value_y_f = value_y_c + 55; // Position for Fahrenheit value
  int ambient_label_y = value_y_f + 40; // Position for Ambient label
  int ambient_value_y = ambient_label_y + 15; // Position for Ambient value

  // Object Temperature
  gfx->setTextSize(1);
  gfx->setTextColor(WHITE);
  gfx->setCursor(10, label_y);
  gfx->println("Object:");

  gfx->setTextSize(4);
  gfx->setTextColor(RED);
  gfx->setCursor(10, value_y_c);
  gfx->print(objTemp, 1);
  gfx->println(" C");

  // Fahrenheit conversion
  gfx->setTextSize(2);
  gfx->setTextColor(ORANGE);
  gfx->setCursor(10, value_y_f);
  gfx->print((objTemp * 9.0 / 5.0) + 32.0, 1);
  gfx->println(" F");

  // Ambient Temperature
  gfx->setTextSize(1);
  gfx->setTextColor(WHITE);
  gfx->setCursor(10, ambient_label_y);
  gfx->println("Ambient:");

  gfx->setTextSize(2);
  gfx->setTextColor(GREEN);
  gfx->setCursor(10, ambient_value_y);
  gfx->print(ambTemp, 1);
  gfx->println(" C");
}

void drawSensorErrorScreen() {
  gfx->fillScreen(BLACK);
  gfx->setCursor(10, 80);
  gfx->setTextSize(2);
  gfx->setTextColor(RED);
  gfx->println("Sensor Error!");
  gfx->setCursor(10, 110);
  gfx->println("Check wiring &");
  gfx->setCursor(10, 140);
  gfx->println("I2C address.");
}

// --- Main Arduino Functions ---

void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.println("SETUP: Starting...");

  Wire.begin(SDA_PIN, SCL_PIN);

  Serial.println("SETUP: Finding MLX90614 sensor...");
  if (!mlx.begin()) {
    Serial.println("SETUP: ERROR - Failed to find MLX90614 sensor.");
    pinMode(LCD_BL, OUTPUT);
    digitalWrite(LCD_BL, HIGH);
    gfx->begin();
    drawSensorErrorScreen();
    while (1);
  }
  Serial.println("SETUP: MLX90614 sensor found.");

  Serial.println("SETUP: Initializing Display...");
  pinMode(LCD_BL, OUTPUT);
  digitalWrite(LCD_BL, HIGH);
  gfx->begin();
  Serial.println("SETUP: Display initialized.");

  pinMode(TRIGGER_PIN, INPUT_PULLUP);
  Serial.println("SETUP: Button initialized.");

  Serial.println("SETUP: Drawing ready screen.");
  drawReadyScreen();
  Serial.println("SETUP: Complete. Starting loop.");
}

void loop() {
  static unsigned long lastHeartbeat = 0;
  if (millis() - lastHeartbeat > 5000) {
    Serial.println("LOOP: Heartbeat - loop is running.");
    lastHeartbeat = millis();
  }

  int reading = digitalRead(TRIGGER_PIN);
  if (reading != lastButtonState) {
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading == LOW && currentState == READY) {
      Serial.println("LOOP: Button pressed. Starting measurement.");
      currentState = MEASURING;
      lastMeasurementTime = millis();
      drawMeasuringScreen();
    }
  }
  lastButtonState = reading;

  switch (currentState) {
    case READY:
      break;

    case MEASURING:
      if (millis() - lastMeasurementTime > 500) {
        Serial.println("LOOP: Taking temperature reading...");
        float objectTemp = mlx.readObjectTempC();
        float ambientTemp = mlx.readAmbientTempC();

        Serial.print("LOOP: Object Temp: "); Serial.print(objectTemp); Serial.println(" C");
        Serial.print("LOOP: Ambient Temp: "); Serial.print(ambientTemp); Serial.println(" C");

        drawResultScreen(objectTemp, ambientTemp);
        currentState = DISPLAY_RESULT;
        lastMeasurementTime = millis();
      }
      break;

    case DISPLAY_RESULT:
      if (millis() - lastMeasurementTime > resultDisplayTime) {
        Serial.println("LOOP: Result display timeout. Returning to ready.");
        currentState = READY;
        drawReadyScreen();
      }
      break;
  }
}

Credits

Arnov Sharma
358 projects • 368 followers
I'm Arnov. I build, design, and experiment with tech—3D printing, PCB design, and retro consoles are my jam.

Comments