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.

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

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 requiring a recompile. The ability to write rules allow for interesting behaviors between sensors and peripherals, such as updating an attached display when sensor values change.

Rules are defined within a rule set. Tasmota out of the box has 3 rule sets called rule1, rule2, and rule3. Any number of rules can be placed in each set (though, limited by the device's memory, but the documentation says at least 1000 bytes). Rule sets can be enabled or disabled, which also enables or disables any rules within. This allows for entire behaviors to be enabled or disabled simply by enabling or disabling a rule set.

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 could possibly want to update a display when the PPM value changes.

  on MHZ19B#CarbonDioxide do DisplayText [zc1l1]%value%ppm endon

Rules are always defined within a rule set and always begin with 'on' and end with 'endon'. In this example, the rule is defined within rule1 and would run DisplayText whenever the sensor's CarbonDioxide metric changes. We can add additional rules within rule1 by appending additional rules as a list. In another example with the INA219, we could write the following rules which will write the voltage, current, and wattage value to a display:

  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

For the rules to take effect, enable the rule set:

rule1 1

For more complex rules that need to run multiple commands, chain them together with the backlog command. Rules can also enable or disable entire rule sets to enable or disable some behavior. Case in point, from one of my other projects, we could enable and later disable the screen from updating when a button is pressed:

  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

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

Some sensors and displays use the I2C protocol. The ESP8266 uses GPIO4 and GPIO5 as the I2C data and clock, respectively. When using I2C devices with Tasmota, configure the template as shown on the right. 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"}

SSD1306 display[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 ESP on GPIOs 4 and 5. 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.

Displaytext commands[edit | edit source]

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 after clearing the screen.

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]

I had a protoboard with a LDR connected to the ADC. The input range is supposed to be 3.3v (in full darkness) to 0.0v (in full brightness). When selecting the ADC input to ADC Light, the light reading is inverted.

There are 4 values: The type (3 for light), resistor constant, lux scalar, and lux exponent. Out of sheer laziness, I opted to just change the values until I got a value that was reasonable and increases with more light. While doing so, 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.

Compiling[edit | edit source]

Compile instructions are available at https://tasmota.github.io/docs/Compile-your-build/. The most simple way I've used is to use their docker image with the following commands:

$ 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

The different builds are available at https://github.com/arendst/Tasmota/blob/063611314777d4dd9dc8c25905f19f8b25f510aa/platformio.ini#L18

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: