Note: We have started the process of preparing a release library of mupplets in project mupplet-core. Currently, neither this project nor the new mupplet-core
are stable,
this is very much ongoing development.
Interfaces and messages in this project are NOT stable or final.
This repository is a testing-ground for new ideas and implementations.
- 2019-11-20: v.0.2.0 Breaking changes to MQTT messages formats and APIs for greater consistency:
led
->light
,luminosity
->illuminance
,/sensor/
prefix for sensor data, led-brightnessluminance
->brightness
.
muwerk applets; mupplets: functional units that support specific hardware or reusable applications.
mupplets use muwerks MQTT-style messaging to pass information between each other on the same device. If connected to an MQTT-server via munet, all functionallity is externally available through an MQTT server such as Mosquitto.
See mupplet led and switch example for a complete example that illustrates how a switch mupplet (interfacing to a physical button) and a led mupplet (infacing to a physical led) communicate using muwerk. Both switch and led are also accessible via external MQTT without extra code. Additionally, both switch and led auto-register with Home Assistant [optional, if available], so they can be controlled using Home Assistant, Siri or Alexa.
Code in this project is generally MIT licensed, but this partly depends on included third-party libraries. Please verify licenses of third-party libraries.
For ESP8266 and ESP32, it is recommended to use munet for network connectivity.
Note: third-party libraries may be subject to different licensing conditions.
mupplet | Function | Hardware | Dependencies | Platform | Home Assistant |
---|---|---|---|---|---|
airq_bme280.h | Temperature, Humidity, Pressure | Adafruit BM2680 | Adafruit BME280 Library, Adafruit unified sensor | ESP, ESP32 | yes |
airq_bme680.h | Air quality ("gas resistance"), Temperature, Humidity, Pressure | Adafruit BME680 | Adafruit BME680 Library, Adafruit unified sensor | ESP, ESP32 | yes |
airq_bsec_bme680.h | Air quality ("gas resistance"), Temperature, Humidity, Pressure | BME680 | based on proprietary BSEC Software Library, see also BOSCH BSEC library | ESP, ESP32 | yes |
airq_ccs811.h | Air quality sensor CO2, VOC | CCS811 | SparkFun CCS811 Arduino Library | ESP, ESP32 | yes |
clock7seg.h | Simple 4 digit clock with timer | 4x 7segment display with HT16K33 | Adafruit GFX Library Adafruit LED Backpack Library | ESP | |
digital_out.h | GPIO output | switch external hardware via GPIO | ESP, ESP32 | yes | |
i2c_pwm.h | 16 channel PWM via I2C | PCA9685 based I2C 16 channel board | https://github.com/adafruit/Adafruit-PWM-Servo-Driver-Library | ESP | |
illuminance_ldr.h | Illuminance | LDR connected to analog port | ESP, ESP32 | yes | |
illuminance_tsl2561.h | Illuminance | Adafruit TSL2561 | Wire, Adafruit unified sensor, Adafruit TSL2561 | ESP, ESP32 | yes |
led.h | LED diode | Digital out or PWM connected to led | ESP, ESP32 | yes | |
mp3.h | MP3 player | OpenSmart v1.1 OpenSmart MP3 player | ESP, ESP32 | ||
neocandle.h | butterlamp sim | Adafruit neopixel feather wing | Adafruit Neopixel | ||
power_bl0397.h | Power meter | BL0937 sensor chip for power, volt, amp | ESP, ESP32 | yes | |
pressure.h | Air pressure and temperature sensor | BMP085, BMP180 | Adafruit BMP085 unified, Adafruit unified sensor | ESP, ESP32 | yes |
pressure_bmp280.h | Air pressure and temperature sensor | BMP280 | Adafruit BMP280, Adafruit unified sensor | ESP, ESP32 | yes |
shift_reg_74595.h | serial to parallel output | 74HC595 shift register(s) | ESP, ESP32 | ||
switch.h | Button | any push button | ESP, ESP32 | yes | |
temperature_gy906.h | IR and ambient temperature | GY-906 / MLX90614 I2C Sensor | https://github.com/adafruit/Adafruit-MLX90614-Library | ESP, ESP32 | yes |
temperature_mcp9808.h | High precision temperature | MCP9808 I2C Sensor | https://github.com/adafruit/Adafruit_MCP9808_Library | ESP, ESP32 | yes |
temp_hum_dht.h | Temperature, humidity sensor | DHT 11, DHT 21, DHT 22 | DHT sensor library, Adafruit unified sensor | ESP, ESP32 | yes |
Note: Home Assistent, if support is yes
, the device can be auto-registered using Home Assistant's MQTT discovery functionality by calling myMupplet.registerHomeAssistant("muppletFriendlyName");
The illuminance_ldr mupplet measures illuminance using a simple analog LDR (light dependent resistor)
topic | message body | comment |
---|---|---|
<mupplet-name>/sensor/unitilluminance |
normalized illuminance [0.0-1.0] | Float value encoded as string |
#include "illuminance_ldr.h"
ustd::Scheduler sched;
ustd::Ldr ldr("myLDR",A0);
void task0(String topic, String msg, String originator) {
if (topic == "myLDR/sensor/unitilluminance") {
Serial.print("Illuminance: ");
Serial.prinln(msg); // String float [0.0, ..., 1.0]
}
}
void setup() {
ldr.begin(&sched);
sched.subscribe(tID, "myLDR/sensor/unitilluminance", task0);
ldr.registerHomeAssistant("Living Illuminance"); // Optional auto-discovery for Home Assistant
}
Note: For ESP32 make sure to use a port connected to ADC #1, since ADC #2 conflicts with Wifi and ports connected to ADC #2 cannot be used concurrently with Wifi!
Allows to set LEDs via digital logic or PWM brightness.
Hardware: 330Ω resistor, led.topic | message body | comment |
---|---|---|
<mupplet-name>/light/unitbrightness |
normalized brightness [0.0-1.0] | 0.34 : Float value encoded as string. Not send on automatic changes (e.g. pulse mode) |
<mupplet-name>/light/state |
on or off |
current led state (on is not sent on pwm intermediate values) |
topic | message body | comment |
---|---|---|
<mupplet-name>/light/set |
on , off , true , false , pct 34 , 34% , 0.34 |
Led can be set fully on or off with on/true and off/false. A fractional brightness of 0.34 (within interval [0.0, 1.0]) can be sent as either pct 34 , or 0.34 , or 34% . |
<mupplet-name>/light/mode/set |
passive , pulse <duration_ms> , blink <intervall_ms>[,<phase-shift>] , pattern <pattern>[,<intervall>[,<phase>]] or wave <intervall_ms>[,<phase-shift>] |
Mode passive does no automatic led state changes, pulse switches the led on for <duration_ms> ms, then led goes back to passive mode. blink changes the led state very interval_ms on/off, wave uses pwm to for soft changes between on and off states. Optional comma-speratated phase [0.0, ..., 1.0] can be added as a phase-shift. Two leds, one with wave 1000 and one with wave 1000,0.5 blink inverse. Patterns can be specified as string containing + ,- ,0 ..9 or r . + is led on during <intervall> ms, - is off, 0 ..9 brightness-level. An r at the end of the pattern repeats the pattern. "pattern +-+-+-+++-+++-+++-+-+-+---r,100" lets the board signal SOS. |
Example: sending an MQTT message with topic <led-name>/mode/set
and message wave 1000
causes the led to softly pulse between on and off every 1000ms.
Multiple leds are time and phase synchronized.
#include "led.h"
uint8_t channel=0; // only ESP32, 0..15
ustd::Led led("myLed",D5,false,channel);
// Led connected to pin D5,
// false: led is on when D5 low
// (inverted logic)
// Each led for ESP32 needs a unique PWM channel 0..15.
// messages are sent/received to myLed/light/...
void setup() {
led.begin(&sched);
led.setmode(led.Mode::WAVE, 1000);
// soft pwm pulsing in 1000ms intervals
// same can be accomplished by publishing
// topic myLed/light/setmode msg "wave 1000"
// OPTIONAL: Register with Home Assistant, led name is 'toy led'.
led.registerHomeAssistant("Toy led");
- See mupplet led and switch example for a complete example.
- See mupplet led and switch home assistant example for an example with Home Assistant switch integration.
Support switches with automatic debouncing.
Hardware: 330Ω resistor, led, switch.topic | message body | comment |
---|---|---|
<mupplet-name>/switch/state |
on , off or trigger |
switch state, usually on or off . In modes falling and rising only trigger |
messages are sent on rising or falling signal. | ||
<mupplet-name>/switch/debounce |
reply to <mupplet-name>/switch/debounce/get , switch debounce time in ms [0..1000]ms. |
|
<custom-topic> |
on , off or trigger |
|
<mupplet-name>/switch/shortpress |
trigger |
Switch is in duration mode, and button is pressed for less than <shortpress_ms> (default 3000ms). |
<mupplet-name>/switch/longpress |
trigger |
Switch is in duration mode, and button is pressed for less than <longpress_ms> (default 30000ms), yet longer than shortpress. |
<mupplet-name>/switch/verylongtpress |
trigger |
Switch is in duration mode, and button is pressed for longer than <longpress_ms> (default 30000ms). |
<mupplet-name>/switch/duration |
<ms> |
Switch is in duration mode, message contains the duration in ms the switch was pressed. |
topic | message body | comment |
---|---|---|
<mupplet-name>/switch/set |
on , off , true , false , toggle |
Override switch setting. When setting the switch state via message, the hardware port remains overridden until the hardware changes state (e.g. button is physically pressed). Sending a switch/set message puts the switch in override-mode: e.g. when sending switch/set on , the state of the button is signalled on , even so the physical button might be off. Next time the physical button is pressed (or changes state), override mode is stopped, and the state of the actual physical button is published again. |
<mupplet-name>/switch/mode/set |
default , rising , falling , flipflop , timer <time-in-ms> , duration [shortpress_ms[,longpress_ms]] |
Mode default sends on when a button is pushed, off on release. falling and rising send trigger on corresponding signal change. flipflop changes the state of the logical switch on each change from button on to off. timer keeps the switch on for the specified duration (ms). duration mode sends messages switch/shortpress , if button was pressed for less than <shortpress_ms> (default 3000ms), switch/longpress if pressed less than <longpress_ms> , and switch/verylongpress for longer presses. |
<mupplet-name>/switch/debounce/set |
String encoded switch debounce time in ms, [0..1000]ms. Default is 20ms. This is especially need, when switch is created in interrupt mode (see comment in example). |
#include "led.h"
#include "switch.h"
ustd::Scheduler sched;
ustd::Led led("myLed",D5,false);
ustd::Switch toggleswitch("mySwitch",D6, ustd::Switch::Mode::Default, false);
void switch_messages(String topic, String msg, String originator) {
if (topic == "mySwitch/switch/state") {
if (msg=="on") {
led.set(true);
} else {
led.set(false);
}
}
}
void setup() {
led.begin(&sched);
toggleswitch.begin(&sched);
toggleswitch.setMode(ustd::Mode::Flipflop);
sched.subscribe(tID, "mySwitch/switch/state", switch_messages);
// Optional: register with Home Assistant:
toggleswitch.registerHomeAssistant("Super switch");
}
- See mupplet led and switch example for a complete example.
- See mupplet led and switch home assistant example for an example with Home Assistant switch integration.
Measures temperature and humidity.
Hardware: 10kΩ, DHT22 sensor.topic | message body | comment |
---|---|---|
<mupplet-name>/sensor/temperature |
<temperature> |
Float, encoded as String, temperature in Celsius. "23.3" |
<mupplet-name>/sensor/humidity |
<humidity> |
Float, encoded as String, humidity in percent. "55.6" |
#include "temp_hum_dht.h"
ustd::Scheduler sched(10,16,32);
ustd::Dht dht("myDht",D4);
void sensor_messages(String topic, String msg, String originator) {
if (topic == "myDht/sensor/temperature") {
Serial.println("Temperature: "+msg);
}
if (topic == "myDht/sensor/humidity") {
Serial.println("Humidity: "+msg);
}
}
void setup() {
Serial.begin(115200);
dht.begin(&sched);
sched.subscribe(tID, "myDht/sensor/#", sensor_messages);
}
See Temperature and humidity for a complete example.
Allows to control up to 16 PWM leds or servos.
Hardware: PCA9685 based I2C-PWM board.- This mupplet can either be in mode
Mode::PWM
orMode::SERVO
. InPWM
mode, the pwm frequency is 1000Hz by default, inSERVO
mode, frequency is 60Hz. All 16 channels share the same mode. The global pwm frequency can be overriden withsetFrequency(freq)
. - Servo minma and maxima can be configured with
void setServoMinMax(int minP=150, int maxP=600) { // pulses out of 4096 at 60hz (frequency)
. See Adafruit's excellent documentation for more details on servo callibration. - Max 25mA per channel!
topic | message body | comment |
---|---|---|
<mupplet-name>/i2cpwm/set/<channel-no> |
on , off , true , false , pct 34 , 34% , 0.34 |
For leds, results in set fully on or off with on/true and off/false. A fractional brightness of 0.34 (within interval [0.0, 1.0]) can be sent as either pct 34 , or 0.34 , or 34% . For Mode::SERVO this results in a servo-position proportional to the value [0..1]. |
#include "i2c_pwm.h"
ustd::Scheduler sched(10,16,32);
ustd::I2CPWM servo("myServo",ustd::I2CPWM::Mode::SERVO);
double count=0.0;
void appLoop() { // change servo every 500ms
servo.setUnitLevel(15,count); // change servo at channel 15.
count+=0.2;
if (count>1.0) count=0.0;
}
void setup() {
int tID = sched.add(appLoop, "main", 500000); // 500ms schedule for appLoop task
servo.begin(&sched);
}
See Servo for a complete example.
Allows playback of different MP3 files.
Hardware: OpenSmart MP3 player (e.g. AliExpress).⚠️ While the documentation says that the player works from 3.3 to 5V, the amplifier does not seem to work with less than 5V. It is unclear what voltage is generated for player's TX line. I had no problems connecting to 3.3V logic, but this is not documented as being safe.- The mupplet uses non-blocking asynchronous serial read and write. Each command messages is separated automatically and asynchronously by 120ms from the closed following messages to prevent confusing the mp3 player.
⚠️ The player sometimes sends completely undocumented messages (MQTT topicxmessage
). Do not rely too much on a deterministic message protocol.
- SD-Card needs to be fat or fat32 format.
- It can have 100 folders (
00
...99
), and each folder can contain 256 files (000-xxx.mp3
...255-yyy.mp3
), a file can be accessed with folder and track number:01/002-mysong.mp3
would be the file identified by folder 1 (=directory name01
) and track 2 (filename002xxx.mp3
).
topic | message body | comment |
---|---|---|
<mupplet-name>/mediaplayer/track/set |
folder-id ,track-id |
To play file 01/002-mysong.mp3, message text should contain 1,2 . |
<mupplet-name>/mediaplayer/state/set |
play ,pause ,stop |
Stops, pauses or plays current song. |
<mupplet-name>/mediaplayer/volume/set |
0...30 | Sets playback volume to between 0 and 30(max). |
topic | message body | comment |
---|---|---|
<mupplet-name>/mediaplayer/volume |
0...30 | Current playback volume |
<mupplet-name>/mediaplayer/storage |
NONE ,DISK ,TF-CARD ,SPI |
Active storage type |
<mupplet-name>/mediaplayer/state |
STOP ,PLAY ,PAUSE ,FASTFORWARD ,FASTREWIND ,PLAYING |
Current player state. State PLAYING is not defined in documentation and seems to be followed always by state PLAY . |
<mupplet-name>/mediaplayer/xmessage |
hexdump |
Undocumented messages. |
// Do NOT use serial debug, since Serial is used for communication with MP3 player!
//#define USE_SERIAL_DBG 1
#include "mp3.h"
ustd::Scheduler sched(10,16,32);
ustd::Mp3Player mp3("mp3", &Serial, ustd::Mp3Player::MP3_PLAYER_TYPE::OPENSMART);
void setup() {
mp3.begin(&sched);
mp3.setVolume(4);
mp3.playFolderTrack(1,1);
}
See MP3 for a complete example.
Allows serial output of 8 bit (or more, if cascaded) data using either 3 GPIOs or SPI (MOSI, CLK, +latch).
Hardware: OpenSmart MP3 player (e.g. AliExpress).Can be combined with ULN2003 (7 outputs) or ULN2803 (8 outputs) darlington amplifiers to drive higher voltage or current loads.
topic | message body | comment |
---|---|---|
<mupplet-name>/shiftreg/set/all |
byte |
Set output to value of byte . |
<mupplet-name>/shiftreg/set/<bit-no> |
on , off , true , false , 0, 100 |
Set output bit bit-no to high or low. |
<mupplet-name>/shiftreg/pulse/<bit-no> |
on[,pulse-time] |
Set output bit bit-no to high for pulse-time milliseconds. |
topic | message body | comment |
---|---|---|
<mupplet-name>/shiftreg |
<byte> |
Current value of shift register as string (in decimal). |