From Leo's Notes
Last edited on 19 March 2023, at 19:42.

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[edit | edit source]

Obtain ESPTool and the latest Tasmota binary. Use ESPTool.py to erase and load Tasmota via serial.

ESP8266 (Wemos D1)[edit | edit source]

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                                                                                                                                                                                                                                                        
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  
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.    

Hard resetting via RTS pin...

ESP32[edit | edit source]

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
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
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.

Hard resetting via RTS pin...

Configuration[edit | edit source]

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.

  1. Setup the device template. Remember to check off 'activate' or else the template won't get applied.
  2. Configure MQTT. Set the host as mqtt, port as 1883, topic as the device name, and the prefix as tasmota/%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[edit | edit source]

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[edit | edit source]

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[edit | edit source]

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:

  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

System#Init - Run when system starts, before WiFi and MQTT

System#Boot - Run when the system starts, after WiFi and MQTT

System#Save - Run before we reboot

Time#Minute - Run every minute

Trigger on a specific trigger if the value meets the condition [TriggerName]#[ValueName][comparison][value] Time#Minute|5 - Run every 5 minutes

Time#Minute=241 - Run at 04:01AM every day

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.

  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[edit | edit source]

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.

 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

 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

 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[edit | edit source]

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[edit | edit source]

Tasmota I2C GPIO Configuration
Tasmota I2C GPIO Configuration for a ESP8266

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[edit | edit source]

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[edit | edit source]

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


#ifndef USE_DISPLAY_SSD1306
#define USE_DISPLAY_SSD1306

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[edit | edit source]

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[edit | edit source]

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[edit | edit source]

Compile Tasmota with the INA219 sensor enabled by defining the following in the user_config_override.h file:

#ifndef USE_I2C
#define USE_I2C

#ifndef USE_INA219
#define USE_INA219

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.

  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

  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

  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[edit | edit source]

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[edit | edit source]

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[edit | edit source]

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[edit | edit source]

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[edit | edit source]

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[edit | edit source]

Peripherals are prefixed in the source tree with xdrv (driver), xdsp (display), xlgt (lights), xnrg (energy), xsns (sensors).

Creating a new sensor driver[edit | edit source]

See: https://tasmota.github.io/docs/Sensor-API/

After creating the sensor driver file, edit the configurations header file and recompile: