Tasmota
Tasmota is an open source firmware for the ESP8266 and ESP32. It is mostly used for home automation devices, but can also be loaded on any other ESP device. It comes with support for a wide range of peripherals 'out of the box' and makes interacting with sensors via WiFi extremely easy as a result.
Flashing Tasmota
Obtain ESPTool and the latest Tasmota binary. Use ESPTool.py to erase and load Tasmota via serial.
ESP8266 (Wemos D1)
Here is what I did to flash a Wemos D1, for example.
$ python -m esptool --port /dev/ttyUSB0 erase_flash
esptool.py v3.0
Serial port /dev/ttyUSB0
Connecting....
Detecting chip type... ESP8266
Chip is ESP8266EX
Features: WiFi
Crystal is 26MHz
MAC: 50:02:91:fe:9d:a4
Uploading stub...
Running stub...
Stub running...
Erasing flash (this may take a while)...
Chip erase completed successfully in 8.6s
Hard resetting via RTS pin...
$ python -m esptool --port /dev/ttyUSB0 write_flash -fs 1MB -fm dout 0x0 tasmota.bin
esptool.py v3.0
Serial port /dev/ttyUSB0
Connecting....
Detecting chip type... ESP8266
Chip is ESP8266EX
Features: WiFi
Crystal is 26MHz
MAC: 50:02:91:fe:9d:a4
Uploading stub...
Running stub...
Stub running...
Configuring flash size...
Compressed 613568 bytes to 438208...
Wrote 613568 bytes (438208 compressed) at 0x00000000 in 38.7 seconds (effective 126.8 kbit/s)...
Hash of data verified.
Leaving...
Hard resetting via RTS pin...
ESP32
Download the ESP32 bootloader files from https://github.com/arendst/Tasmota-firmware/tree/main/static/esp32, the latest Tasmota firmware file, and then run:
# esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after hard_reset \
write_flash -z --flash_mode dout --flash_freq 40m --flash_size detect \
0x1000 bootloader_dout_40m.bin \
0x8000 partitions.bin \
0xe000 boot_app0.bin \
0x10000 tasmota32.bin
esptool.py v3.2
Serial port /dev/ttyUSB0
Connecting.....
Chip is ESP32-D0WDQ6 (revision 1)
Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None
Crystal is 40MHz
MAC: f0:08:d1:d4:04:48
Uploading stub...
Running stub...
Stub running...
Changing baud rate to 921600
Changed.
Configuring flash size...
Auto-detected Flash size: 4MB
Flash will be erased from 0x00001000 to 0x00004fff...
Flash will be erased from 0x00008000 to 0x00008fff...
Flash will be erased from 0x0000e000 to 0x0000ffff...
Flash will be erased from 0x00010000 to 0x0015ffff...
Compressed 15536 bytes to 10862...
Wrote 15536 bytes (10862 compressed) at 0x00001000 in 0.3 seconds (effective 397.7 kbit/s)...
Hash of data verified.
Compressed 3072 bytes to 129...
Wrote 3072 bytes (129 compressed) at 0x00008000 in 0.0 seconds (effective 588.8 kbit/s)...
Hash of data verified.
Compressed 8192 bytes to 47...
Wrote 8192 bytes (47 compressed) at 0x0000e000 in 0.1 seconds (effective 751.8 kbit/s)...
Hash of data verified.
Compressed 1372432 bytes to 964850...
Wrote 1372432 bytes (964850 compressed) at 0x00010000 in 14.3 seconds (effective 766.9 kbit/s)...
Hash of data verified.
Leaving...
Hard resetting via RTS pin...
Configuration
After a factory reset, you will need to configure its WiFi settings manually by connecting to the WifiManager created access point. After it connects to the proper network, connect to it again through a web browser.
The following section applies to my own network and setup. For my own network, because the IoT devices are all in its own isolated network, I need to do a SSH tunnel in order to access the web interface. This is done by running: ssh -L 8080:192.168.1.x:80 root@iot-router
and then connecting to localhost:8080
in a web browser. In the web interface, there are a few things that needs to be configured in order for it to work properly with the NodeRED/MQTT service outside of the IoT network.
- Setup the device template. Remember to check off 'activate' or else the template won't get applied.
- Configure MQTT. Set the host as
mqtt
, port as1883
, topic as the device name, and the prefix astasmota/%topic%/
. The mqtt hostname resolves to the MQTT server and the IoT router allows MQTT traffic on port 1883 to leave the network to this server.
MQTT and hostname
When configuring the Tasmota hostname and MQTT strings, you may substitute variable names. This table outlines what you can use.
Description | What you type | What you get |
---|---|---|
%06X | 0353C0 | |
last 4 digits of mac | %04d | |
hostname | %s | |
MQTT prefix | %prefix% | One of: cmnd , stat , tele
|
MQTT topic | %topic% | User defined. Eg: WP5-1
|
Rules
Rules are sets of commands that should run when a specific condition is met. They are a way to program user defined behaviors to Tasmota without needing to write real code and requiring a recompile. The ability to write rules allow for interesting behaviors between events, sensors, and peripherals, such as updating an attached display when sensor values change, or turning a light on when a specific condition is met.
Rules are defined within a rule set which allows similar rules to be grouped togoether. Out of the box, Tasmota has 3 rule sets called rule1
, rule2
, and rule3
. Any number of rules can be placed in each set (though in actuality, it's limited by the device's memory. Tasmota's documentation says each rule set can hold least 1000 bytes worth of rules). Each of the 3 rule sets can be enabled or disabled. When a rule set is enabled, all rules within it will be active and vice versa. The ability to toggle on or off entire sets of rules allows for some interesting behaviors which we will explore later on.
More can be found on Tasmota's Rules documentation.
Quick start with rules
Frequently, we want to do something with a sensor reading. In the case of a CO2 monitor, we may want to update a display when the PPM value changes. To do this, we create a new rule within the rule1 set by the following command:
Rule1
on MHZ19B#CarbonDioxide do DisplayText [zc1l1]%value%ppm endon
Rules are defined within a rule set and always begin with on
and end with either endon
or break
. A break causes stops the rule set from processing any further rules, which can be useful for behaviors that depend on a nested list of conditions (more on this later).
The trigger comes after the on keyword. Types of triggers include the following:
Trigger when... | Syntax | Examples |
---|---|---|
Trigger on a specific trigger, such as timers, clocks, MQTT events, system events, variable changes, etc. | [TriggerName]#[ValueName]
|
Event#Update - Triggered when we run a custom event event update
|
Trigger on a specific trigger if the value meets the condition | [TriggerName]#[ValueName][comparison][value]
|
Time#Minute|5 - Run every 5 minutes
|
A sensor value changes | [SensorName]#[ValueName]
|
MHZ19B#CarbonDioxide
|
A sensor value changes and meets the condition | [SensorName]#[ValueName][comparison][value]
|
MHZ19B#CarbonDioxide>600
|
Tasmota supports the following comparison operators. Aside from the string based equality operators, I haven't come across any of the other string operators in any rule examples yet.
Numeric | String |
---|---|
Equal numeric a==b
|
Equal strings a=b
|
Not equal a!=b
|
Not equal a$!a
|
Greater a>b
|
Starts with a$<b
|
Less than a<b
|
Ends with a$>b
|
Modulo a|b
|
Contains a$|b
|
Does not contain a$^b
|
After the trigger, we specify the tasks we want to do after the do
keyword. If you need to run multiple commands, use the backlog
command to combine multiple commands together separated by a semicolon. You can add as many rules within a rule set as you can fit within the confines of your device's memory.
Here's a slightly more complex set of rules. Notice how we can enable and disable rule sets within a rule.
Rule1
on INA219#Voltage do DisplayText [c1l1]V %value% V endon
on INA219#Current do DisplayText [c1l2]I %value% A endon
on INA219#Power do DisplayText [c1l3]P %value% W endon
on button1#state=3 do backlog power1 1; rule1 1 endon
on button1#state=10 do backlog rule1 1; power1 1; delay 500; power1 0; rule1 0 endon
To apply these rules, copy and paste the entire rule set and paste it as a command into Tasmota's console. This multi-line rule set will be converted into a single line and compressed in memory by Tasmota.
Next, enable the rule set for the rules to take effect by running:
rule1 1
Creating a timed auto-off switch
Using rules, we can easily create a switch that will automatically turn itself off after a some time. We can even extend this behavior by having different on-durations based on the number of button presses. The following rules were used to set up a switch that automatically turned the bathroom fan off using the Gosund KS602 smart switch.
rule1
on button1#state=3 do backlog power1 on; ruletimer1 0; rule1 0; rule2 1 endon
on button1#State=10 do backlog power1 on; ruletimer1 1800; rule1 0; rule2 1; delay 10; ledpower1 off; ledstate 1; endon
on button1#state=11 do backlog power1 on; ruletimer1 3600; rule1 0; rule2 1; delay 10; ledpower1 off; ledpower1 on; ledpower1 off; ledstate 1; endon
on button1#state=12 do backlog power1 on; ruletimer1 7200; rule1 0; rule2 1; delay 10; ledpower1 off; ledpower1 on; ledpower1 off; ledpower1 on; ledpower1 off; ledstate 1; endon
on button1#state=13 do backlog power1 on; ruletimer1 14400; rule1 0; rule2 1; delay 10; ledpower1 off; ledpower1 on; ledpower1 off; ledpower1 on; ledpower1 off; ledpower1 on; ledpower1 off; ledstate 1; endon
rule2
on Rules#Timer=1 do backlog power1 off; rule2 0; rule1 1 endon
on button1#state do backlog power1 off; ruletimer1 0; rule2 0; rule1 1 endon
rule3
on system#boot do backlog SetOption73 1; SetOption32 20; switchmode 8 endon
To simplify the logic, I use a simple state machine where rule1
is the off state and rule2
is the on state. When the switch is off, it accepts different number of button presses to select the duration of the on state. The button states are: 3 is for hold, 10 for 1 press, 11 for 2 presses, and so on up to 4 presses. The RuleTimer1
is used to trigger Rule2's timer=1
rule that turns the switch off after a set number of seconds. At the end of each rule, I added a series of LED flashes to show what mode was selected.
Rule3 is used to enforce certain SetOptions
on boot (though it should be saved to flash). SwitchMode 8
is required so that only these rules toggle the state of the relay.
Configuring peripherals
There are a different set of supported peripherals out of the box depending on which Tasmota build you used. Refer to the BUILDS page at https://github.com/arendst/Tasmota/blob/development/BUILDS.md to determine if a particular feature is enabled. If it isn't, you will need to compile Tasmota with the desired feature enabled using the users_config_override.h
file placed in the Tasmota source directory.
Setting up I2C
Some sensors and displays use the I2C protocol which requires 2 wires for data and clock.
The ESP8266 uses GPIO 4 and GPIO 5 while the ESP32 uses GPIO 21 and GPIO 22 for SDA and SCL, respectively. You can set these under the 'Configure Module' page on the web interface.
Save and activate the template. If configured properly, the i2cscan
command should return a list of addresses that was found on the I2C bus.
CMD: i2cscan
MQT: tasmota/device/RESULT = {"I2CScan":"Device(s) found at 0x3c 0x40"}
Displays
All supported displays can be found at https://tasmota.github.io/docs/Displays/#supported-displays. You may need to tweak the Tasmota compile options and rebuild a firmware file for your particular display. Once Tasmota is able to see your display, you should be able to draw or write text to it using the common display commands that are included in Tasmota.
See also: https://github.com/tasmota/docs/blob/master/docs/Displays.md
SSD1306
The SSD1306 is an OLED display that comes in various sizes. These displays use I2C. The Wemos D1 OLED module is one such device with a screen size of 64x48. Tasmota supports this display through the tasmota-displays.bin
image or you may compile support in by creating a user_config_override.h
file with:
#ifndef USE_I2C
#define USE_I2C
#endif
#ifndef USE_DISPLAY
#define USE_DISPLAY
#endif
#ifndef USE_DISPLAY_SSD1306
#define USE_DISPLAY_SSD1306
#endif
After flashing Tasmota with the proper peripheral support, attach the display to the ESP8266 via I2C (GPIOs 4 and 5 for SDA/SCL on the ESP8266, or GPIOs 21 and 22 for SDA/SCL on the ESP32). Once connected, running i2cscan
on the console should return the display device at address 0x3c
.
05:26:20.151 CMD: i2cscan
05:26:20.173 MQT: tasmota/D1-2/RESULT = {"I2CScan":"Device(s) found at 0x3c"}
Next, configure the display in Tasmota by running:
DisplayAddress 0x3c # the address discovered by i2cscan
DisplayModel 2 # for SSD1306
DisplayWidth 64 # the display resolution
DisplayHeight 48
DisplayDimmer 100 # turn the display on
DisplayMode 0 # enter text mode
Save and restart Tasmota (with Restart 1
) and then confirm that the display is working by running Displaytext [zl1c1]hello
.
HD44780 LCD display via i2c
The HD44780 family of LCD displays, which includes the 1602 LCD display, can be interfaced using a i2c adapter. After connecting the adapter to Tasmota via the i2c bus, you should be able to start drawing text to the display using the Displaytext command.
To get started, use the display Tasmotao firmware or compile Tasmota with USE_I2C
, USE_DISPLAY
options. Configure the display with the following commands:
DisplayAddress 0x27 # the address discovered by i2cscan
DisplayModel 1 # for LCD display
DisplayWidth 16 # the display resolution
DisplayHeight 2
DisplayDimmer 100 # turn the display on
DisplayMode 0 # enter text mode
The special characters that are available to the HD44780 can be written to by escaping the hex code with tilda. Eg. ~e4 to write the character 0xe4.
Displaytext commands
All text is drawn using the Displaytext command.
You may have noticed that there are some letters in square brackets passed to the Displaytext
command. These are options that control the behavior of where and how the text should be drawn and can control the font size and location of the text. For example, to print "Hello World" across two separate lines, we write hello on line 1 column 1 (l1c1
) and world on line 2 column 1 (l2c1
):
Displaytext [l1c1]hello
Displaytext [l2c1]world!
There are many other parameters which can be found at: https://tasmota.github.io/docs/Displays/#displaytext-use. The most important ones you should know are listed below. You may chain any number of these together and they will run in the order they appear. Eg. [zl1c1f0s1]Hello
would clear, then write the world Hello
in the smallest font on line 1 column 1.
Command/parameter | Result | Example |
---|---|---|
l# | line (1..) | l1 |
c# | column (1..) | c1 |
f# | font size (0..) | f0 |
s# | font scaling (1..) | s1 |
z | clears screen | |
o / O | turns off / on the display |
With the power of Tasmota rules, we can trigger text to be written when a state changes. In the case of a CO2 monitor, we can create the following rule which clears and prints the PPM value on the first line of the display whenever the value changes.
Rule1 on MHZ19B#CarbonDioxide do DisplayText [zc1l1]%value%ppm endon
Rule1 1
This rule would print "400ppm" if the CO2 reading was at 400, for example.
For more information, see the rules section above.
INA219
Compile Tasmota with the INA219 sensor enabled by defining the following in the user_config_override.h
file:
#ifndef USE_I2C
#define USE_I2C
#endif
#ifndef USE_INA219
#define USE_INA219
#endif
Configure the GPIO pins for I2C as usual. If everything is working, Tasmota should being reporting the read voltage and current from the INA219 in the telemetry messages in addition to it being displayed on the web page. If no values are being reported, run i2cscan
and confirm whether the device shows up on the bus and recheck wiring and the I2C GPIO pin configuration.
For my INA219 monitoring board, I also have a SSD1306 display. I use the following rules to show the values on the display. A button is also used to optionally turn the display on temporarily.
Rule1
on INA219#Voltage do DisplayText [c1l1]V %value% V endon
on INA219#Current do DisplayText [c1l2]I %value% A endon
on INA219#Power do DisplayText [c1l3]P %value% W endon
on ANALOG#A0 do Backlog scale1 %value%, 0, 1024, 0, 5.65; DisplayText [c1l4]B %var1% V endon
Rule1 1
Rule2
ON button1#state=3 do backlog power1 1; rule1 1 endon
ON button1#state=10 do backlog rule1 1; power1 1; delay 500;delay 500;delay 500;delay 500;delay 500;delay 500; power1 0; rule1 0 endon
Rule2 1
Rule3
ON system#boot DO DisplayMode 0; DisplayDimmer 100 endon
Rule3 1
Backlog VoltRes 2; WattRes 2
Backlog ButtonTopic 0; SetOption73 1; SetOption32 20
Adjusting the shunt resistor to measure higher currents
The INA219 can be adjusted to measure higher currents by changing the shunt resistor. The shunt resistor value can be defined by setting the value to Sensor13
(see: https://github.com/arendst/Tasmota/pull/7803).
To increase the range of the INA219 from the default 3.2A to 6.4A, we could add an additional 0.1Ω resistor in parallel to the existing one which would effectively halve the resistance to 0.05Ω. Update the resistance value in Tasmota by running Sensor13 51
. This should automatically save to flash and reboot.
Light Dependent Resistors (LDR) / photoresistors to the ADC input
The ADC input pins can detect a voltage and convert it into a digital value. Tasmota has the option to read a ADC pin and convert the value into a luminance value measured in Lux.
A full voltage (1V directly to the ESP8266 or 3.3v if you're using the Wemos D1 board) registers as full brightness to no voltage (0V) as full darkness. The min/max/scaling of lux values can be adjusted by the AdcParam 3,arg1,arg2,arg3
command, where arg1 is the resistor constant, arg2 is the lux scalar, and arg3 is the lux exponent used in the Lux equation.
On my own custom Wemos D1 boards, I inadvertently inverted the inputs (so 0v was full brightness). I noticed that the bridge resistor value adjusts the magnitude of the lux reading, the lux scalar adjusts the offset, and the lux exponent adjusts the weight of the input reading. What seemed to work for me was the ADC param values: adcparam 3,3,10,1
.
RGB LED
Tasmota can control 3 separate PWM channels as a single RGB LED light using the MultiPwm feature.
On the hardware side, connect your LED to your ESP on PWM capable output pins. On Tasmota, configure the pin as 'PWM' or 'PWM_i' with each pin on a separate channel (1 as red, 2 as green, 3 as blue). You should use PWM if your pins will be sourcing current (for common-cathode lights) and PWM_i if your pins will be sinking current (for common-anode lights). Ensure that SetOption68 is disabled so that all 3 channels are treated as a single light.
Once this is done, you should see a color selector on the main web interface.
Power meter calibration
See also: Lenovo SE-341AC
Connect a kill-a-watt or other power meter and attach a load to the plug. Use the voltageset and currentset commands to calibrate its power measurements.
I read 122 volts and 0.441 amps, so I ran the following in the console:
voltageset 122
currentset 441
If the power factor doesn't seem to agree, you can use the powerset command to set the active power reading. The active power is usually the value that your reference meter / kill-a-watt is reporting. In my case, I have a LED bulb using 53.6 watts with a power factor of ~0.9 while the smart meter is reporting 60 watts at 1.0 power factor. To correct this, I ran:
powerset 53.6
This should correct the power factor being reported down to ~0.9.
Compiling
Compile instructions are available at https://tasmota.github.io/docs/Compile-your-build/. The simplest way for me was to use their Docker image which uses platform.io to do the builds. To get started, run:
$ git clone https://github.com/tasmota/docker-tasmota
$ cd docker-tasmota
$ docker build -t docker-tasmota .
$ git clone https://github.com/arendst/Tasmota.git
$ docker run -ti --rm -v $(pwd)/Tasmota:/tasmota -u $UID:$GID docker-tasmota -e tasmota
What you want to build is controlled by the environment (provided by the -e
option). A list of all available environments can be found in the platformio.ini file. By default, if no environment is specified, it will build everything for you. For a standard Tasmota firmware, use the tasmota
environment.
To include additional drivers (like displays, sensors, etc), edit the tasmota/my_user_config.h
file and uncomment (to add) or comment out (to remove) each feature as desired. Be aware that Tasmota groups features together and you may need to enable an entire group of features (eg. displays, power monitoring) for a specific feature to become included.
To compile a ESP32 version, pass in -e tasmota32
.
Once the compile is complete, you'll find your firmware under the build_output
directory.
Project structure
Peripherals are prefixed in the source tree with xdrv (driver), xdsp (display), xlgt (lights), xnrg (energy), xsns (sensors).
Creating a new sensor driver
See: https://tasmota.github.io/docs/Sensor-API/
After creating the sensor driver file, edit the configurations header file and recompile: