🇮🇩 Versi Bahasa Indonesia tersedia di README_ID.md
Croaster is a lightweight, open-source temperature monitoring system built on ESP-based microcontrollers. Designed for coffee roasting enthusiasts and professionals, it reads from two thermocouple sensors (Bean Temperature and Environment Temperature) and displays real-time data on a compact OLED screen. Croaster connects seamlessly to popular roasting software via WiFi (WebSocket) and BLE (ESP32 only), making it compatible with both desktop and mobile roasting apps.
Current Firmware Version: 0.51
- ☕ Croaster - Open Source Coffee Roaster Monitor
- 📑 Table of Contents
- 🚀 Features
- 🧩 Hardware Components
- 🔌 Wiring Diagram
- 🛠 Software Architecture
- 📦 Libraries & Dependencies
- 🔧 How to Build and Upload
- 🔗 WiFi Setup Guide
- 📡 Communication Overview
- 🔌 How to Connect Croaster with Artisan
- ⬆️ OTA (Over-The-Air) Updates
- 🧪 Custom Commands
- 📘 License
- ❤️ Contributing
- 🔗 Related Links
- Supports NodeMCU ESP8266 (WiFi only)
- Supports ESP32C3 Super Mini (WiFi & BLE)
- Real-time monitoring of two MAX6675 thermocouple sensors:
- BT — Bean Temperature (inside the drum)
- ET — Environment Temperature (exhaust/inlet)
- Rate of Rise (RoR) calculation for both BT and ET, updated automatically
- Temperature unit switching: Celsius or Fahrenheit
- Configurable data send interval (default: every 3 seconds)
- Built-in temperature smoothing (smoothing factor: 5) to reduce sensor noise
- Visual output on a 128×64 OLED display (SSD1306, I2C)
- WiFi communication via WebSocket on port 81, compatible with:
- Artisan Roaster Scope — industry-standard roasting logger
- ICRM app — companion mobile app (Android)
- BLE communication (ESP32 only) for the ICRM app
- OTA (Over-The-Air) firmware updates via WebSocket (WiFi) and BLE (ESP32 only)
- WiFiManager captive portal for easy WiFi credential setup — no re-flashing needed
- Unique device naming based on chip ID (e.g.
Croaster-A1B2) - Dummy mode for development and testing without physical sensors
- Custom JSON command system via the centralized
CommandHandlerclass - Easily extendable with user-defined commands
| Component | Description |
|---|---|
| 1× NodeMCU ESP8266 or ESP32C3 Super Mini | Main microcontroller |
| 1× 128×64 OLED display (SSD1306, I2C) | Real-time temperature display |
| 2× MAX6675 thermocouple modules | SPI-based K-type thermocouple ADC |
| 2× K-type thermocouple probes | Temperature probes (BT & ET) |
All components run on 3.3V. Ensure your power supply can handle the combined current draw of both sensors and the display.
| NodeMCU ESP8266 | ESP32C3 Super Mini | |
|---|---|---|
| OLED Display | GND → GND | GND → GND |
| VCC → 3.3V | VCC → 3.3V | |
| SCL → D1 | SCL → GPIO9 | |
| SDA → D2 | SDA → GPIO8 | |
| ⠀ | ||
| ET Sensor (Environment Temp) | GND → GND | GND → GND |
| VCC → 3.3V | VCC → 3.3V | |
| SCK → D5 | SCK → GPIO4 | |
| SO → D7 | SO → GPIO5 | |
| CS → D6 | CS → GPIO6 | |
| ⠀ | ||
| BT Sensor (Bean Temp) | GND → GND | GND → GND |
| VCC → 3.3V | VCC → 3.3V | |
| SCK → D5 | SCK → GPIO4 | |
| SO → D7 | SO → GPIO5 | |
| CS → D8 | CS → GPIO7 |
Both sensors share the SCK and SO lines (SPI bus). They are distinguished by their individual CS pins.
Croaster uses a clean, modular C++ architecture built with the Arduino framework. Each subsystem is encapsulated in its own class:
| Module | File | Responsibility |
|---|---|---|
CroasterCore |
CroasterCore.h/.cpp |
Sensor reading, RoR calculation, temperature smoothing, data state |
DisplayManager |
DisplayManager.h/.cpp |
OLED rendering loop, status screens |
CommandHandler |
CommandHandler.h/.cpp |
JSON command parsing and dispatching (BLE & WebSocket) |
WebSocketManager |
WebSocketManager.h/.cpp |
WebSocket server, data broadcast, OTA trigger |
BleManager |
BleManager.h/.cpp |
BLE server, characteristic notify, command receive (ESP32 only) |
OtaHandler |
OtaHandler.h/.cpp |
Binary OTA update handling over WebSocket and BLE |
WiFiManagerUtil |
WiFiManagerUtil.h/.cpp |
WiFiManager captive portal setup and lifecycle |
DeviceIdentity |
DeviceIdentity.h/.cpp |
Chip ID, device name, IP address helpers |
MAX6675 Sensors → CroasterCore (read + smooth + RoR)
↓
┌────────────┴────────────┐
WebSocketManager BleManager (ESP32)
↓ ↓
Artisan / ICRM ICRM (Android)
| Library | Purpose |
|---|---|
| arduinoWebSockets | WebSocket server |
ArduinoJson ^7.4.3 |
JSON command parsing and serialization |
Adafruit SSD1306 ^2.5.16 |
OLED display driver |
MAX6675_Thermocouple ^2.0.2 |
Thermocouple sensor reading |
WiFiManager ^2.0.17 |
Captive portal WiFi setup |
| ESP32 BLE Arduino (built-in ESP32 core) | BLE server & characteristics |
-
Install PlatformIO (VS Code extension or CLI)
-
Clone the repository:
git clone git@github.com:IiemB/Croaster.git cd Croaster -
Review
platformio.iniand select your target environment -
Upload the firmware:
# For ESP8266 pio run -e esp8266 -t upload # For ESP32C3 pio run -e esp32c3 -t upload
Note: ESP32C3 Super Mini uses a custom partition scheme (
custom32c3sm.csv) to maximize app storage. See references.md for setup details.
Arduino IDE is required if you use the Makergo ESP32C3 SuperMini board definition, which is not yet fully supported by PlatformIO.
-
Run the conversion script to copy source files into the Arduino sketch folder:
./copy_to_ino.sh
-
Open the
croaster-arduino/folder in Arduino IDE 2.x -
Select your board:
- ESP8266 →
NodeMCU 1.0 (ESP-12E Module) - ESP32C3 →
Makergo ESP32C3 SuperMini
- ESP8266 →
-
For ESP32C3, select the partition scheme:
- Use
Huge APPfor maximum sketch size (OTA not supported) - Use
Custom SuperMinifor OTA support (see references.md for setup)
[!NOTE] The
Huge APPpartition does not support OTA via the ICRM App. To enable OTA, follow the custom partition steps in references.md. - Use
-
Install all required libraries via Arduino Library Manager (see Libraries & Dependencies)
-
Upload via
Sketch → Upload
Croaster uses WiFiManager to handle WiFi credentials without re-flashing. On first boot (or after erasing credentials), Croaster creates its own access point:
- On your phone or computer, connect to the WiFi network named
[XXXX] Croaster-XXXX - A captive portal will open automatically — enter your home WiFi SSID and password
- Croaster will save the credentials and connect automatically on subsequent boots
- The IP address assigned to Croaster is shown on the OLED display
For a visual walkthrough, see: ➡️ How to Connect to WiFi - YouTube
- Port:
81 - Protocol: WebSocket (text frames for JSON commands, binary frames for OTA)
- Data format: JSON, broadcast every
intervalSendseconds (default: 3s) - Compatible with Artisan Roaster Scope and ICRM app (Android)
- Service UUID:
1cc9b045-a6e9-4bd5-b874-07d4f2d57843 - Data Characteristic UUID:
d56d0059-ad65-43f3-b971-431d48f89a69 - Supports notify (data push) and write (command receive)
- Compatible with the ICRM app (Android only)
You can connect your Croaster device to Artisan using either a direct WiFi connection or through your home/local WiFi network.
Use this method when Croaster is not connected to any WiFi network, or when you want a direct peer-to-peer connection.
-
On your computer, connect to the WiFi network broadcasted by Croaster (e.g.
[XXXX] Croaster-XXXX) -
Open Artisan → Config → Port
-
Set the configuration as shown below:
Use this method when Croaster is already connected to your home/office WiFi network.
-
Make sure your laptop and Croaster are on the same WiFi network
-
Open Artisan → Config → Port
-
Enter the IP address shown on the Croaster OLED display (or via serial monitor)
-
Set the configuration as shown:
Croaster supports firmware updates without a USB cable, via the ICRM app over WebSocket (WiFi) or BLE (ESP32 only).
- OTA is handled by the
OtaHandlerclass, which receives binary firmware data in chunks and returns a JSON progress payload after each chunk - Progress is shown on the OLED display during the update
- BLE OTA includes timeout checks to handle stalled transfers
- OTA requires the custom partition scheme (
custom32c3sm) on ESP32C3 — theHuge APPpartition does not support OTA - After a successful OTA update, Croaster restarts automatically
Croaster accepts JSON-formatted commands over both WebSocket and BLE. The CommandHandler class dispatches all incoming commands.
All commands use the "command" key. Basic (string) commands:
| Command JSON | Action |
|---|---|
{"command": "restartesp"} |
Restarts the device |
{"command": "erase"} |
Erases WiFi credentials and restarts |
{"command": "displayToggle"} |
Toggles the OLED display on/off |
{"command": "rotateScreen"} |
Rotates the OLED screen 180° |
{"command": "dummyOn"} |
Enables dummy/test mode (no real sensors needed) |
{"command": "dummyOff"} |
Disables dummy mode |
{"command": "blink"} |
Blinks the built-in LED |
{"command": "getDeviceInfo"} |
Returns device info (IP, SSID, firmware version) |
{"command": "getExtra"} |
Returns extra user-defined data |
Configuration commands use a nested JSON object under "command":
| Command JSON | Action |
|---|---|
{"command": {"tempUnit": "F"}} |
Switches temperature unit to Fahrenheit |
{"command": {"tempUnit": "C"}} |
Switches temperature unit to Celsius |
{"command": {"interval": 5}} |
Sets data send interval to 5 seconds |
{"command": {"correctionBt": 1.5, "correctionEt": -0.5}} |
Applies temperature correction offset |
{"command": {"wifiConnect": {"ssid": "MyWiFi", "pass": "password"}}} |
Connects to a specified WiFi network |
To add a basic (string) command, add a new else if branch inside handleBasicCommand in CommandHandler.cpp. To add a configuration command, add a new condition inside handleJsonCommand. Both methods receive a parsed JsonObject, so you can read any key/value from the JSON payload.
MIT License — free for personal and commercial use. Contributions welcome!
Pull requests, bug reports, and feature requests are welcome! Feel free to open an issue or submit a PR on GitHub.
- ICRM App — companion Android app for Croaster
- Artisan Roaster Scope — open-source coffee roasting logger
- WiFi Setup Video — quick visual guide
- References & Advanced Setup — custom partitions, OTA, PlatformIO tips
- FAQ — frequently asked questions


