Power consumption of different RS485 to TTL modules (Part2/3)

In this article, I will check the power consumption of different common RS485 to TTL modules. Please note that this is the total consumption of the module including LED indicators for some of the modules.

I will be using the same setup as part 1 with the temperature humidity sensor. Baud-rate:9600, 80 bit (include starting and ending bit) request and then a 90 bit reply data.

The six RS485 to TTL modules I will be using.

1. MAX485 module

Typical operating voltage: 5V
Sleep mode: No
Slew-rate limiting: No
Fail-safe circuitry: Output short-circuit protection, thermal shutdown
AutoDirection control: No
Data Rate: 2.5 (Mbps)
ESD-Protection: –
Module LED indicator: Power LED (always ON)
Maxim Integrated Datasheet: here

MAX485 Block Diagram
5V setup with Arduino Mega

Average transmission current: 112.6mA
Average idling current: 4.7mA
Consumption time: 8.5ms (only consume power during requesting)

3.3V setup with ESP32

***The MAX485 module requires 5V power input as mentioned by the manual, please take your own risk when using 3.3v. But the communication looks normal with my own test, I test ran it for 60 mins***

Average transmission current: 35.86mA
Average idling current: 2.32mA
Consumption time: 8.5ms (only consume power during requesting)

2. SP3485E module

Typical operating voltage: 3.3V, 5V logic tolerant (module power input 3V-30V)
Sleep mode: R̅E̅ high and DE low for 600ns
Slew-rate limiting: No
Fail-safe circuitry: Driver output short-circuit protection
AutoDirection control: No (Yes, controlled by 74HC04 provided by the module)
Data Rate: 10 (Mbps)
ESD-Protection: 2kV
Module LED indicator: TXD indicator, RXD indicator
MaxLinear Datasheet: here

SP3485 Block Diagram

Let’s zoom in on the reply region and have a closer look. The current chart matches the RS485 signal.

Average transmission current: 1.67mA (requesting: 2.46mA, replying 1.11mA)
Average idling current: 321uA

3. MAX13487EESA

Typical operating voltage: 5V
Sleep mode: S̅H̅D̅N̅ pin
Slew-rate limiting: Yes
Fail-safe circuitry: TTL side hot swapping protection
AutoDirection control: Yes
Data Rate: 0.5 (Mbps)
ESD-Protection: 15kV
Module LED indicator: Power LED (always ON), TXD indicator, RXD indicator
Maxim Integrated Datasheet: here

MAX13487EESA Block Diagram

***The MAX13487EESA module requires 5V power input as mentioned by the manual, please take your own risk when using 3.3v. But the communication looks normal with my own test, I test ran it for 60 mins***

Average transmission current: 6.26mA (requesting: 6.67mA, replying:5.93mA)
Average idling current: 5.5mA

4. SCM3721ASA signal isolation YD3082EESA module

Typical operating voltage: 3V-5.5V
Sleep mode: R̅E̅ high and DE low
Slew-rate limiting: Yes
Fail-safe circuitry: Receiver pulls high when receiver’s differential inputs are either shorted, open circuit, or connected to a terminal resistor
AutoDirection control: No (Yes, controlled by HC14 provided by the module)
Data Rate: 1 (Mbps)
ESD-Protection: 15kV
Module LED indicator: None
Datasheet: here

YD3082EESA Block Diagram

Average transmission current: 4.5mA (requesting: 5.27mA, replying:3.8mA)
Average idling current: 3.12mA

5. SCM3725ASA signal isolation SCM3406ASA module

Typical operating voltage: 3V-5.5V
Sleep mode: R̅E̅ high and DE low
Slew-rate limiting: No
Fail-safe circuitry: Receiver pulls high when receiver’s differential inputs are either shorted, open circuit, or connected to a terminal resistor
AutoDirection control: No (Yes, controlled by HC14 provided by the module)
Data Rate: 10 (Mbps)
ESD-Protection: 15kV
Module LED indicator: None
Datasheet: here

SCM3406A Block Diagram

Average transmission current: 6.84mA (requesting: 7.66mA, replying:6.09mA)
Average idling current: 5.19mA

***2 transmissions failed out of 1358 trials ***

6. ADUM5401 DC-DC isolated SP485EE module

Typical operating voltage:5V (module power input 3.3V-5V)
Sleep mode: No
Slew-rate limiting: No
Fail-safe circuitry: Driver output short-circuit protection
AutoDirection control: No (Yes, controlled by HC14 provided by the module)
Data Rate: 10 (Mbps)
ESD-Protection: 15kV
Module LED indicator: Power indicator, TXD indicator, RXD indicator
Datasheet: here

SP485E Block Diagram

Average transmission current: 109.71mA (requesting: 120.72mA, replying:102.06mA)
Average idling current: 72.27mA

Summary

Most of the modules work stable and reliable under my test environment and the code mentioned in the previous article, only the SCM3406ASA module has transmission failure, but only a small portion.

The Max485 is a no go in industrial applications, because it has no ESD protection but the same time high power consumption (But the MAX485 is the only few chips that offer industry qualifications such as MIL-STD-883B). Besides, the MAX485 chip is not any cheaper than the other chip such as MAX481. However, this MAX485 module is extremely cheap compared to the other modules. This could be a good starting point to test out your first RS485 circuit.

The ADUM54, SP485EE module is DC-DC power and signal isolated. If you are working in a harsh environment and do not have power contain, this module may be your choice.

The MAX13487EESA chip is the only chip that has auto direction control and hot-swaps protection. If you want to make your own module and do not want to add any hex inverter, this may be your choice.

The SP3485 module consumes relatively low power, if you are working with a power limited environment, this is your first go. And in part 3, we will dive deeper into SP3485 chip and make our own PCB.

Chinese Text generation for LILYGO T5-4.7 inch E-Paper ESP32

There is a python script on the LilyGo-EPD47 GitHub for text generation. But the program is not optimized for Chinese words, one big reason is that the Struct GFXfont uses Unicode interval to provide a range of characters. We usually won’t import the whole Chinese dictionary into ESP32 because there are way too many words. Instead, we will search the Unicode for the corresponding Chinese characters and only import the words which will be displayed.

And that comes to a problem when using the fontconvert.py. Usually, those Chinese words we are using will not have a close Unicode number. Take 像素(pixel)as an example, these 2 Chinese words have a Unicode of U+50CF and U+7D20. And there are already 11346 words in between, a full set of fonts could easily cover more than twenty thousand characters which the esp32 just doesn’t have enough memory to handle.

I am using these two very useful links to inspect the font file and check the corresponding Unicode of any characters. For example, I want to import a sentence “最大像素 and esp32”, found out their Unicode: 0x6700, 0x5927, 0x50CF, 0x7D20 and sort them in ascending order. Finally, load them into a modified version of fontconvert.py.

#!python3
from code import interact
import freetype
import zlib
import sys
import re
import math
import argparse
from collections import namedtuple

parser = argparse.ArgumentParser(description="Generate a header file from a font to be used with epdiy.")
parser.add_argument("name", action="store", help="name of the font.")
parser.add_argument("size", type=int, help="font size to use.")
parser.add_argument("fontstack", action="store", nargs='+', help="list of font files, ordered by descending priority.")
parser.add_argument("--compress", dest="compress", action="store_true", help="compress glyph bitmaps.")
args = parser.parse_args()

GlyphProps = namedtuple("GlyphProps", ["width", "height", "advance_x", "left", "top", "compressed_size", "data_offset", "code_point"])

font_stack = [freetype.Face(f) for f in args.fontstack]
compress = args.compress
size = args.size
font_name = args.name

# inclusive unicode code point intervals
# must not overlap and be in ascending order
intervals = [
    [32, 126],
    [160, 255],
    [0x50CF],
    [0x5927],
    [0x6700],
    [0x7D20],

    # (0x2500, 0x259F),
    # (0x2700, 0x27BF),
    # # powerline symbols
    # (0xE0A0, 0xE0A2),
    # (0xE0B0, 0xE0B3),
    # (0x1F600, 0x1F680),
]
newintervals = []

def norm_floor(val):
    return int(math.floor(val / (1 << 6)))

def norm_ceil(val):
    return int(math.ceil(val / (1 << 6)))

for face in font_stack:
    # shift by 6 bytes, because sizes are given as 6-bit fractions
    # the display has about 150 dpi.
    face.set_char_size(size << 6, size << 6, 150, 150)

def chunks(l, n):
    for i in range(0, len(l), n):
        yield l[i:i + n]

total_size = 0
total_packed = 0
all_glyphs = []

def load_glyph(code_point):
    face_index = 0
    while face_index < len(font_stack):
        face = font_stack[face_index]
        glyph_index = face.get_char_index(code_point)
        if glyph_index > 0:
            face.load_glyph(glyph_index, freetype.FT_LOAD_RENDER)
            return face
            break
        face_index += 1
        print (f"falling back to font {face_index} for {chr(code_point)}.", file=sys.stderr)
    raise ValueError(f"code point {code_point} not found in font stack!")

for interval in intervals:
    if len(interval) <= 1:
        interval.append(interval[0]+1)
    newintervals.append(interval)


for i_start, i_end in newintervals:
    for code_point in range(i_start, i_end + 1):
        face = load_glyph(code_point)
        bitmap = face.glyph.bitmap
        pixels = []
        px = 0
        for i, v in enumerate(bitmap.buffer):
            y = i / bitmap.width
            x = i % bitmap.width
            if x % 2 == 0:
                px = (v >> 4)
            else:
                px = px | (v & 0xF0)
                pixels.append(px);
                px = 0
            # eol
            if x == bitmap.width - 1 and bitmap.width % 2 > 0:
                pixels.append(px)
                px = 0

        packed = bytes(pixels);
        total_packed += len(packed)
        compressed = packed
        if compress:
            compressed = zlib.compress(packed)

        glyph = GlyphProps(
            width = bitmap.width,
            height = bitmap.rows,
            advance_x = norm_floor(face.glyph.advance.x),
            left = face.glyph.bitmap_left,
            top = face.glyph.bitmap_top,
            compressed_size = len(compressed),
            data_offset = total_size,
            code_point = code_point,
        )
        total_size += len(compressed)
        all_glyphs.append((glyph, compressed))

# pipe seems to be a good heuristic for the "real" descender
face = load_glyph(ord('|'))

glyph_data = []
glyph_props = []
for index, glyph in enumerate(all_glyphs):
    props, compressed = glyph
    glyph_data.extend([b for b in compressed])
    glyph_props.append(props)

print("total", total_packed, file=sys.stderr)
print("compressed", total_size, file=sys.stderr)

print("#pragma once")
print("#include \"epd_driver.h\"")
print(f"const uint8_t {font_name}Bitmaps[{len(glyph_data)}] = {{")
for c in chunks(glyph_data, 16):
    print ("    " + " ".join(f"0x{b:02X}," for b in c))
print ("};");

print(f"const GFXglyph {font_name}Glyphs[] = {{")
for i, g in enumerate(glyph_props):
    print ("    { " + ", ".join([f"{a}" for a in list(g[:-1])]),"},", f"// {chr(g.code_point) if g.code_point != 92 else '<backslash>'}")
print ("};");

print(f"const UnicodeInterval {font_name}Intervals[] = {{")
offset = 0
for i_start, i_end in newintervals:
    print (f"    {{ 0x{i_start:X}, 0x{i_end:X}, 0x{offset:X} }},")
    offset += i_end - i_start + 1
print ("};");

print(f"const GFXfont {font_name} = {{")
print(f"    (uint8_t*){font_name}Bitmaps,")
print(f"    (GFXglyph*){font_name}Glyphs,")
print(f"    (UnicodeInterval*){font_name}Intervals,")
print(f"    {len(newintervals)},")
print(f"    {1 if compress else 0},")
print(f"    {norm_ceil(face.size.height)},")
print(f"    {norm_ceil(face.size.ascender)},")
print(f"    {norm_floor(face.size.descender)},")
print("};")