Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dingtian relay board driver #17032

Merged
merged 4 commits into from
Nov 10, 2022
Merged

Conversation

barbudor
Copy link
Contributor

@barbudor barbudor commented Nov 9, 2022

Description:

Related issue (if applicable): fixes #16797

Dingtian relay boards are ESP32 based boards with 8, 16, 24 or 32 relays and inputs.
Relays are driven through x595 shift registers and inputs are read from x165 shift registers
Because the design is using some GPIO for both '595 and '165, those are not independant and could not be managed through the existing Shift595 driver

This driver is not included in any official Tasmota build. You must compile your own build by adding the following line in your user_config_override.h:

#define USE_DINGTIAN_RELAY

The driver define 5 News GPIOs (from here):

|                   |  8-relay | 16-relay | 32-relay | Description                                                |
|-------------------+----------+----------+----------+------------------------------------------------------------|
| GPIO_DINGTIAN_CLK | GPIO 14  | GPIO 14  | GPIO 14  | Serial clokc for both 595 and 165                          |
|    CLK Index      |    1     |    2     |   4      | Number of 74HC165 and 74HC595                              |
| GPIO_DINGTIAN_SDI | GPIO 13  | GPIO 13  | GPIO 13  | Serial input for 595                                       |
| GPIO_DINGTIAN_Q7  | GPIO 16  | GPIO 35  | GPIO 35  | Serial output of 165                                       |
| GPIO_DINGTIAN_PL  | GPIO 32  | GPIO 0   | GPIO 0   | Latch 165 inputs (but also controls OE of the 595)         |
| GPIO_DINGTIAN_RCK | GPIO 15  | GPIO 15  | GPIO 15  | Latch of 595 outputs (but also control CLK inhibit of 165) |

The CLK GPIO supports index 1 to 4 to specifiy the number of shift registers:

  • 1 : 8 relays/inputs boards
  • 2 : 16 relays/inputs boards
  • 3 : 24 relays/inputs boards (not really existing)
  • 4 : 32 relays/inputs boards

Exemple for the 8 relay board:
image
image

WebGUI:
image

Inputs state are reported through SENSOR message at teleperiod like for PCF8574/MCP230xx port extenders:

21:46:07.760 MQT: tele/dingtian1/SENSOR = {"Time":"2022-10-29T21:46:07+02:00","DINGTIAN":{"IN1":1,"IN2":1,"IN3":1,"IN4":1,"IN5":1,"IN6":1,"IN7":1,"IN8":1}} (retained)

And changes are reported to DINGTIAN_CHG topic:

21:46:50.665 MQT: stat/dingtian1/DINGTIAN_CHG = {"Time":"2022-10-29T21:46:50+02:00","DINGTIAN_CHG":{"IN1":0,"IN2":0,"IN3":0,"IN4":0,"IN5":0,"IN6":0,"IN7":0,"IN8":0}}

The driver is limited to ESP32 as the Dingtian boards only use ESP32, however if needed the limit could be removed in order to use the same principle on a ESP8266 custom board.

Checklist:

  • The pull request is done against the latest development branch
  • Only relevant files were touched
  • Only one feature/fix was added per PR and the code change compiles without warnings
  • The code change is tested and works with Tasmota core ESP8266 V.2.7.4.9
  • The code change is tested and works with Tasmota core ESP32 V.2.0.5
  • I accept the CLA.

NOTE: The code change must pass CI tests. Your PR cannot be merged unless tests pass

@arendst arendst merged commit 7c6962c into arendst:development Nov 10, 2022
@barbudor barbudor mentioned this pull request Dec 26, 2022
6 tasks
@barbudor barbudor deleted the dingtian_driver branch December 29, 2022 19:48
@jeroenst
Copy link
Contributor

jeroenst commented Mar 17, 2023

I can confirm this also works correctly for this board on the DTWONDER store (request for programmable board because the seller has his own firmware flashed and locked the esp32 for reading and writing) :
https://nl.aliexpress.com/item/4001232791244.html?spm=a2g0o.order_list.order_list_main.17.21ef79d2INZqkQ&gatewayAdapt=glo2nld

One question, how can I invert the inputs? Now they are 1 when the contact is open en 0 when the contact is closed, I prefer 1 for closed, 0 for open.

@jeroenst
Copy link
Contributor

I found an issue with this, the output relays are clicking every 50ms because the output shift register latches every 50ms also when there is no change in the outputs.

@barbudor
Copy link
Contributor Author

Looks like it is the same board
As of today the input state exactly mimic the logical state on the pin : Low => 0, High => 1
It's not possible to change it without changing the driver code for now.

What I was thinking is that we can now implement such input as virtual switches, fully support Switchmode command which could be convenient.
I can do that this week-end if I can find the time

The click issue you are encountering is due to the hardware.
The problem is that they have connected the Latch signal of the input shift register to the OE pin of the 595.
There is NOTHING that can be done on the software side. You need to modify the board to have it working properly:

  • disconnect '595 OE pin from GPIO and get it directly tighted to GND

@jeroenst
Copy link
Contributor

jeroenst commented Mar 17, 2023

  • disconnect '595 OE pin from GPIO and get it directly tighted to GND

I was trying this unfortunately I couldn't save the 595 so I ordered some 595's for replacement.

I understand the state, the inputs of the 165 are pulled-up and the input has to be connected to GND in order to pull it down on this board to "activate" the input.

If you want to implement it as virtual switches, I will test it as soon as I fixed my board. I will also create the documentation page on https://tasmota.github.io/docs/Supported-Peripherals/#expanding-specific-devices if desired.

@jeroenst
Copy link
Contributor

What I was thinking is that we can now implement such input as virtual switches, fully support Switchmode command which could be convenient. I can do that this week-end if I can find the time

Is it possible to use the inputs in rules when you implement this?

@barbudor
Copy link
Contributor Author

Like any native "Switch" gpio

@jeroenst
Copy link
Contributor

jeroenst commented Mar 17, 2023

Isn't it an idea to change this code

  // setup
  digitalWrite(Dingtian->pin_rck, 0);    // rclk and clkinh to 0
  digitalWrite(Dingtian->pin_pl, 1);      // load inputs in '165, ready for shift-in (side effect '595 in tri-state)
  for ( int i = Dingtian->count ; i > 0 ; i-- ) {
    // relay out to '595
    digitalWrite(Dingtian->pin_sdi, outputs & 1);
    outputs >>= 1;
    // input from '165
    inputs |= digitalRead(Dingtian->pin_q7) ? in_bit : 0;
    in_bit <<= 1;
    // generate CLK pulse
    digitalWrite(Dingtian->pin_clk, 1);
    digitalWrite(Dingtian->pin_clk, 0);
  }
  // ending
  digitalWrite(Dingtian->pin_rck, 1);    // rclk pulse to load '595 into output registers
  digitalWrite(Dingtian->pin_pl, 0);      // re-enable '595 ouputs

to something like:

  // setup
  digitalWrite(Dingtian->pin_rck, 0);    // rclk and clkinh to 0
  for ( int i = Dingtian->count ; i > 0 ; i-- ) {
    // relay out to '595
    digitalWrite(Dingtian->pin_sdi, outputs & 1);
    outputs >>= 1;
    // generate CLK pulse
    digitalWrite(Dingtian->pin_clk, 1);
    digitalWrite(Dingtian->pin_clk, 0);
  }
  // ending
  digitalWrite(Dingtian->pin_rck, 1);    // rclk pulse to load '595 into output registers
  digitalWrite(Dingtian->pin_pl, 1);      // load inputs in '165, ready for shift-in (side effect '595 in tri-state)
  digitalWrite(Dingtian->pin_pl, 0);      // re-enable '595 ouputs
  for ( int i = Dingtian->count ; i > 0 ; i-- ) {
    // input from '165
    inputs |= digitalRead(Dingtian->pin_q7) ? in_bit : 0;
    in_bit <<= 1;
    // generate CLK pulse
    digitalWrite(Dingtian->pin_clk, 1);
    digitalWrite(Dingtian->pin_clk, 0);
  }

In this case the latch takes the minimum of time (about 122ns according to https://www.esp32.com/viewtopic.php?t=11678) and it decreases the output tri-state time which is probably less or even not triggering the enabled relais to fall off.

Unfortunately I can't test it because I broke my board and I'm waiting for a new 74HC595 to arrive.

@barbudor
Copy link
Contributor Author

barbudor commented Mar 17, 2023

No
The PL is an asynchronous load: it must remains HI during the whole duration of the shift-out.
As soon as it's back LOW, the inputs (A-H) drive the asynchornous set or reset of the latches.

image

image

image

https://www.ti.com/lit/gpn/sn74hc165

@jeroenst
Copy link
Contributor

Thank you for explaining.

@barbudor
Copy link
Contributor Author

barbudor commented Mar 19, 2023

@jeroenst
Can you test from my fork or do you need me to build a binary for you ?
https://github.com/barbudor/Tasmota/tree/digtian_inputs_as_switches

//#define USE_DINGTIAN_RELAY                       // Add support for the Dingian board using 74'595 et 74'165 shift registers
//  #define DINGTIAN_INPUTS_INVERTED               // Invert input states (Hi => OFF, Low => ON)
//  #define DINGTIAN_USE_AS_BUTTON                 // Inputs as Tasmota's virtual Buttons
//  #define DINGTIAN_USE_AS_SWITCH                 // Inputs as Tasmota's virtual Switches

DINGTIAN_INPUTS_INVERTED mostly interresting for Buttons (it also works with switches but you already have SwitchMode to invert Switch behavior)

Only one of DINGTIAN_USE_AS_BUTTON or DINGTIAN_USE_AS_SWITCH

If you own a 32 in/out board, beware that Tasmota is limited to 28 switches so last 4 inputs wouldn't be usable in that case. There is not such limitation with Buttons

If you already have switches or button on native GPIO, index for 1st Dingtian input is shifted (check the logs at level 3)
Same for relays but that was already the case
By default Button X/Switch X is attached to Power X without any rule
Standard rules trigger works (on Switch28#state=1 do ... endon)

As soon as you have tested on the real hardware, I will PR

@jeroenst
Copy link
Contributor

jeroenst commented Mar 19, 2023

18:36:22.004 MQT: 0006/TASMOTA-DTWONDER01/tele/SENSOR = {"Time":"2023-03-19T18:36:21","Switch1":"OFF","Switch2":"OFF","Switch3":"OFF","Switch4":"OFF","Switch5":"OFF","Switch6":"OFF","Switch7":"ON","Switch8":"ON","DINGTIAN":{"IN1":0,"IN2":0,"IN3":0,"IN4":0,"IN5":0,"IN6":0,"IN7":1,"IN8":1}} (retained)

Seems to work in the console, inputs are inverted, and switches do follow state of the inputs. Also switchmode seems to work.

The only thing that doesn't work is using inverted inputs in a berry script, they are not inverted unfortunately: the second line is the output of printing tasmota.get_switches():

19T18:46:59","Switch1":"OFF","Switch2":"OFF","Switch3":"OFF","Switch4":"OFF","Switch5":"OFF","Switch6":"OFF","Switch7":"OFF","Switch8":"OFF","DINGTIAN":{"IN1":0,"IN2":0,"IN3":0,"IN4":0,"IN5":0,"IN6":0,"IN7":0,"IN8":0}} (retained)
18:47:00.056 [true, true, true, true, true, true, true, true]

@barbudor
Copy link
Contributor Author

Ah, and I forgot to remove the original DINGTIAN key in the SENSOR message

When you say the inversion works but not in Berry, did you used the DINGTIAN_INPUTS_INVERTED or is it based on SwitchMode ?
The DINGTIAN_INPUTS_INVERTED should also work in Berry

@jeroenst
Copy link
Contributor

I did use DINGTIAN_INPUTS_INVERTED, changing switchmode doesn't seem to do anything for the berry script

@barbudor
Copy link
Contributor Author

ok, I'm looking into it

@barbudor
Copy link
Contributor Author

Just tried, when using DINGTIAN_INPUTS_INVERTED, I get all switches to "false" in Berry and when not using I get all to "true"
I hve no hardware connected so I believe it sees the SDI as High
So for me it's working fine
I have removed the DINGTIAN part in the SENSOR message but kept the DINGTIAN Inputs on the Web GUI

I will now PR

thanks

@jeroenst
Copy link
Contributor

jeroenst commented Mar 19, 2023

Strange, in my berry script print(tasmota.get_switches()) prints:
19:32:25.874 [true, true, true, true, true, true, true, true]

While the inputs on the main screen are inverted:
DINGTIAN Input0..Input7 00000000

My defines are:
#define USE_DINGTIAN_RELAY // Add support for the Dingian board using 74'595 et 74'165 shift registers
#define DINGTIAN_INPUTS_INVERTED // Invert input states (Hi => OFF, Low => ON)
// #define DINGTIAN_USE_AS_BUTTON // Inputs as Tasmota's virtual Buttons
#define DINGTIAN_USE_AS_SWITCH // Inputs as Tasmota's virtual Switches

@barbudor
Copy link
Contributor Author

When I compile with INVERTED: I see "1" in the Web UI, and Berry gives "false"
When I compile without; I see "0" in the Web UI and Berry gives "true"

Berry considers "True" when the signal is to GND (Low)

@barbudor
Copy link
Contributor Author

New PR : #18223

@jeroenst
Copy link
Contributor

jeroenst commented Mar 19, 2023

When I compile with INVERTED: I see "1" in the Web UI, and Berry gives "false" When I compile without; I see "0" in the Web UI and Berry gives "true"

Berry considers "True" when the signal is to GND (Low)

When console says 1 berry should be true imo...

I also see this behavior, Berry is always inverted comparing to the console. That was not the case before your change for what I remember.

@barbudor
Copy link
Contributor Author

barbudor commented Mar 19, 2023

Before today's change Berry was not able to report the inputs as Switches state as they were not Switches

You can compare with native GPIO Switches. It should be exactly the same.

@jeroenst
Copy link
Contributor

jeroenst commented Mar 19, 2023

You're right, I remember. Still imo when the console says 0 berry should also report false.

Tha #define DINGTIAN_INPUTS_INVERTED does also changes berry behaviour but in different direction.

@barbudor
Copy link
Contributor Author

You can address that to s-hadinger but I wouldn't expect a breaking change now

@jeroenst
Copy link
Contributor

jeroenst commented Mar 19, 2023

Ok, I leave it this way, I can use it although it is a bit misleading.

Thank you very much for your effort!

Mayby @s-hadinger can take a look at this sometime.

@jeroenst
Copy link
Contributor

Possible this explains the inverted behaviour:

tasmota.get_switches | () -> list(bool)Returns as many values as switches are present. true means PRESSED and false means NOT_PRESSED. (Warning: this is the opposite of the internal representation where PRESSED=0)Note: if there are holes in the switch definition, the values will be skipped. I.e. if you define SWITCH1 and SWITCH3, the array will return the two consecutive values for switches 1/3.

@barbudor
Copy link
Contributor Author

You shouldn't have "holes" in your switches/buttons/relays indexes.
It may work when using only native GPIO but will start to do crazy things when mixing with virtual switches/buttons/relays are those are always assigned continuously from the 1 available index.

@dtlzp
Copy link

dtlzp commented Mar 20, 2023

for Dingtian Relay 8/16/32 channel 74hc165 and 74hc595
the key point is read(74hc165) and write(74hc595) at the same time

below is python driver source code for Tasmota

  # Read and write from 165+595 shift register version2
  def write_read(outputs)
    var max_bit = self.count-1
    var inputs = 0

    gpio.digital_write(self.rck, 0)
    gpio.digital_write(self.pl, 1)

    for i:0..max_bit
      gpio.digital_write(self.sdi, outputs & 1)
      outputs >>= 1

      inputs >>= 1
      if( gpio.digital_read(self.q7) )
        inputs |= 0x80;

      gpio.digital_write(self.clk, 0)
      gpio.digital_write(self.clk, 1)
    end

    gpio.digital_write(self.rck, 1)
    gpio.digital_write(self.sdi, 1)

    gpio.digital_write(self.pl, 0)

    return inputs
  end

notice:
please add lock or close irq when call this function

@dtlzp
Copy link

dtlzp commented Mar 20, 2023

this ESP-IDF C source code example 74hc165 and 74hc595 for Dingtian relay
https://github.com/dtlzp/relay_dev_demo/tree/main/gpio/main

gpio.h key code snippets for 8/16/32 channel

#define VGPIO_REFRESH(igpio, ogpio, i_bytes, o_bytes) \
do \
{ \
    VGPIO_ENTER_CRITICAL(); \
    vpio_refresh((igpio), (ogpio), (i_bytes), (o_bytes)); \
    HC595165_PL(DT_LOW);\
    VGPIO_EXIT_CRITICAL(); \
}while(0)

@jeroenst
Copy link
Contributor

jeroenst commented Mar 20, 2023

@dtlzp this tasmota driver already reads and writes the shift registers at the same time.

The problem is that the latch pin (PL in code) of the input shift register 74hc165 is also connected to the output enable pin of the output shift register 74hc595, causing the outputs of the 74hc595 to go to tristate while reading and writing (also at the same time). This is also the problem in your example code.

Only connecting output enable of the 74hc595 to gnd solves this problem.

@jeroenst
Copy link
Contributor

jeroenst commented Mar 20, 2023

When I compile with INVERTED: I see "1" in the Web UI, and Berry gives "false" When I compile without; I see "0" in the Web UI and Berry gives "true"

Berry considers "True" when the signal is to GND (Low)

What about changing

ButtonSetVirtualPinState(Dingtian->key_offset +i, inputs &1);

To

ButtonSetVirtualPinState(Dingtian->key_offset +i, !(inputs &1) );

And the same for switch.

In this way the switch state is the same as the input state.

Of course the real issue is that switch is inversed but changing this would cause serious issues when people upgrade to a version without this inversion.

@dtlzp
Copy link

dtlzp commented Mar 20, 2023 via email

@jeroenst
Copy link
Contributor

jeroenst commented Mar 20, 2023

the ESP-IDF demo source code shows read(74hc165) and write(74hc595) REFRESH by task "gpio_task" every 20ms , not only refresh when in use. is very important create task "gpio_task" "gpio_task" refresh every 20ms

This is already done in Tasmota.

So every 20ms the OE pin of the 74hc595 and the SH pin of the 74hc165 become high for a few microseconds, because they are interconnected. The latch input (SH) of the 74hc165 has to be high when shifting out the bits.

This causes the relays to make noise.

The OE pin of the 74hc595 has to be connected to gnd to prevent the outputs to go tristate.

@jeroenst
Copy link
Contributor

I soldered the 74HC595 today with OE connected to GND and the relays don't make noise anymore.

The only remaining issue is that the relais are on during power on boot but I received an email from dingtian that they made a hardware revision that solves this issue and they also connected OE to GND.

@barbudor
Copy link
Contributor Author

Pin 10 of the 595 is a master reset
Maybe have a look to what it's connected on the board
May need another hack to insure 0 on power on

@jeroenst
Copy link
Contributor

jeroenst commented Mar 28, 2023

Possible RST can be connected to a GPIO of the ESP32, they are low on boot afaik.

I don't have the board available for testing anymore because it's now in use on a remote location to control a heat pump and corresponding valves.

Maybe Dingtian can provide me a test version of the new hardware revision flashed with tasmota to test in the nearby future.

Just buying a board for testing and modifying is a bit expensive with no purpose for it at this moment.

Thank you for all the effort.

My final berry script for controlling this board is on https://github.com/jeroenst/dingtian-heatpump-control

Used template: {"NAME":"Dingtian 8CH Relay Board","GPIO":[1,9408,1,9440,1,1,1,1,1,9760,9728,9856,9792,1,1,1,0,1,1,1,0,1,1,1,0,0,0,0,9824,9952,1,1,1,0,0,1],"FLAG":0,"BASE":1}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants