Looking For Advice For: Non-WiFi Based Solution For Sharing Data Between ESP32's

You can use ESP-Now to connect the devices.
You just need to program the devices yourself, because the ESP-Now is not that well implemented into HA.

I was looking up the details of ESP-Now to see if it would work, but it also seems to have some challenges associated with implementation and doesn’t seem like it will improve the reliability. As I was reading up on it however, I found this in one of your previous posts.

How would you recommend setting up the link between “them” if not using ESP-Now?

You would like them together with a serial connection.
One ESP device on WiFi and another on ESP-Now.
There are also solutions with just one ESP device, but that require the ESP-Now channel and the WiFi channel to be the same and since the channel will be hard coded into the ESP-Now setup, then it means the WiFi channel will be locked too with this setup.
A locked WiFi channel might work with some WiFi setups, but not all, and especially Mesh setups might be an issue.

If you devices comply with the matter definition for a climate device, then you might also look into the Espressif ZeroCode solution.

Do you know if ESPHome supports this natively, what protocol would you use?
I found the following documentation on I2C, UART, SPI custom components, but all of these pages have deprecation warnings.

I2C for example looks promising but the I2C core component documentation is very limited and doesn’t explain how to send or process received messages if they are not from a supported sensor type. So I am not sure how I would use serial (unless I start writing my own C code but was hoping to avoid that as much as possible :sweat_smile: as it’s been a while since I did anything with C haha).

Does this help? ESPHome to ESPHome direct communication? - #21 by KG3RK3N

You need to write your own C code.

I would have thought this would be the best solution if you do really only need one esp. Reduces integration complexity. Which may end up being more reliable.

Edit: Sorry, I misread cannot as can. Facepalm.

Yes thank you, this is definitely helpful!

The drawback of UART is that it can only connect 2 devices (as I understand it). I2C, on the other hand, facilitates connecting multiple slave devices, which would let me connect all. Never the less I think that, unless someone posts a better (pre-made) solution, I am going to try make an I2C equivalent of this project.

Thank you for the link though, will certainly be helpful :slight_smile: Lets see how far I get… And if I get stuck, this is a good fallback to at least half the number of devices connected to wifi :sweat_smile:

Alright, Thanks for the info.

If you are going with I2C then remember to have a common ground or at least a ground that is managed, otherwise your pull ups or downs will fail.

Ive asked myself this same question as i have 47 esphome nodes on my network, which by the way doesnt seem to have any noticeably negative effects. I pondered this questiin though because, i didnt want to wait untill there were negative effects.

So, it sounds like 4 if the esp32 boards just open a vent, corect? How does it open it? Servo?

If one device is the master and its sending commands to 4 valves then just use something like RF. You dont even need an esp32 at each valve.
https://www.amazon.com/DieseRC-Universal-Wireless-Receiver-Transmitter/dp/B098WGK35L/ref=mp_s_a_1_2_sspa?crid=19GLAEOP14JDN&keywords=rf+relay+12v&qid=1707582864&sprefix=rf+relay%2Caps%2C272&sr=8-2-spons&sp_csd=d2lkZ2V0TmFtZT1zcF9waG9uZV9zZWFyY2hfYXRm&psc=1

Dont let the remote confuse you. All you habe to do is copy the rf codes and then transmit them from the master. I did something similar in my garage and the range on these is really good. I can trigger relays in my detached garage from 60’ away no problem.

If you have a way to directly wire these from the master esp32, obviously thats the best option.

RF requires a bit more if you want to be able to handle states.
Just sending the command will only give you assumed state. You need a reply and also regular updates of states to make it work right.

1 Like

You can actually just send RF/IR codes straight across wires if you like! I.e you don’t even need rf/ir transmitters/recievers if you can wire them together.

My dunny project works like that.

But I agree rf/ir is not a great readymade protocol for these reasons.

Modbus looks like an interesting protocol, but doesn’t seem to be a good hardware match.

Took me a little while to get around to this again but I had some time today so here’s an update. I liked the suggestion to

What I particularly liked about this component is that:

  • It lets me build a solution that can be implemented as both a wired connection as well as a local wireless connection.
  • The basic ESPHome component(s) facilitate writing and reading custom commands. This everything can be made through templates.

I might combine this with something similar to the chip select pin of the SPI protocol so I can use a single remote receiver and not worry about overlapping incoming data. This however wouldn’t work with a wireless connection in which case I would go for something similar to the addressing used in I2C. So far I’ve just been playing around with how I want to send custom data. I also still need to read up a lot on all the parameters of the remote transmit and receive components to ensure my custom commands implementation is robust enough to also handle the noise of wireless communication.

The configuration below is just a first try at getting a chip to talk to itself using the remote component. Currently I plan on using the first value in the raw data array as the Id of the slave device and the second to last value as a checksum. With my current wired implementation the transmission signals are very robust and I have yet to see a value get corrupted in transmission. Nevertheless there is probably a whole lot of stuff I can add to improve this.

Here is my current code so far:

remote_receiver:
  pin:
    number: GPIO17
    inverted: false
  dump: raw
  on_raw:
    then:
    - script.execute: 
        id: check_validity
        received_data_array: !lambda return x;

remote_transmitter:
  id: my_transmitter
  pin: GPIO16
  carrier_duty_percent: 100%

switch:
  - platform: template
    name: "wifi"
    internal: True
    restore_mode: ALWAYS_ON
    turn_on_action:
      then:
        - wifi.enable:
    turn_off_action:
      then:
        - wifi.disable:          
  - platform: template
    name: "sendState"
    turn_on_action:
      - script.execute: send_state

globals:
  - id: my_states_array
    type: std::vector<int>
    initial_value: '{500,333,333,333,333,333}'

sensor:
  - platform: template
    id: sensor_1
    name: "Sensor 1 value"
    lambda: |-
      return 0;
    update_interval: never

number:
  - platform: template
    name: "Number 1"
    optimistic: true
    initial_value: 1
    min_value: 1
    max_value: 100
    step: 1
    on_value:
      then:
        - lambda: |-
            id(my_states_array)[2] = x * 10; 
        - script.execute: send_state

script:
  - id: wifi_on
    then:
      - wifi.enable:
      - lambda: |-
          id(my_states_array)[1] = 550; 
  - id: wifi_off
    then:
      - wifi.disable:        
      - lambda: |-
          id(my_states_array)[1] = 880;
  - id: check_validity
    parameters:
      received_data_array: int[]
    then:
    - lambda: |-
        std::vector<int> x = received_data_array; // Example values
        ESP_LOGD("main", "Printing vector contents:");

        int sum_of_array = 0;

        for (size_t i = 0; i < x.size()-2; ++i) {
            x[i] = std::abs(x[i]);
            sum_of_array += x[i];
        };

        x[-1] = std::abs(x[-1]);

        if (sum_of_array == x[-1] ) {
          ESP_LOGD("main", "Checksum Check FAILED!!!!!!!!!!!");
          id(check_validity).stop();
        } else {
          ESP_LOGD("main", "Checksum Check Passed");
        };

        id(sensor_1).publish_state( x[2]/10 );
 
  - id: send_state
    then:
      - remote_transmitter.transmit_raw:
          code: !lambda |-
            std::vector<int> original_values = id(my_states_array); // Example values
            std::vector<int> transmit_values;
            bool make_negative = false;

            // Calculate the sum of all values in the array
            int sum_of_array = 0;
            for (int value : original_values) {
                sum_of_array += value;
            }
            
            // If the array length is an even number at this point, add the number (333) to the end
            if (original_values.size() % 2 == 0) {
                original_values.push_back(333);
            } 

            // Add the calculated value to the end of the array
            original_values.push_back((2000 - (sum_of_array % 2000)) * (make_negative ? -1 : 1));
            ESP_LOGD("main", "CheckSum: %i", (2000 - (sum_of_array % 2000)) * (make_negative ? -1 : 1));

            // Add the number (333 to the end)
            original_values.push_back(333);



            // Iterate over the original values, making every second value negative
            for (int value : original_values) {
                transmit_values.push_back(make_negative ? -value : value);
                make_negative = !make_negative; // Toggle the flag
            }

            // Now transmit the modified array
            return {transmit_values};

I’m actually very pleased with the results so far, might eventually even see if I can make this into an esphome component where an array is defined on 2 devices and then some backend code facilitates communication between two esphome modules so both arrays remain synced, but thats’s a long term future idea haha. Let me first make sure I get my own implementation running, although so far this seems like it will be a good solution (atleast for my use case).

1 Like

Maybe there is some way to reuse or recycle an existing protocol? Either all or some of it?

Like aircon protocols would be able to send numbers (set temperature) and turn switches on and off?

Plus there’s the address and checksum parts which might be able to be leveraged.

Can’t say I know much about the pros/cons of the various protocols. I know pronto is quite flexible (and may be easier to work with over raw?).

Just a thought…

https://esphome.io/api/structesphome_1_1remote__base_1_1_panasonic_data

I found this repo for BLE that allows sending data between esp32 nodes independently of HA or anything else. It also allows for custom commands.

Thanks for the link, I read through quite a few of those protocols already (including pronto) :slight_smile:

I tried using pronto initially because I liked the fact it encoded 4byte chucks that could include text but when I tried to work with it in ESPHome I struggled with sending variable commands. Maybe I did something wrong but the error logs led me to believe that I might have to make adjustments to the backend code that generates the protocol should I wanted to send commands that weren’t predefined in the code. As I couldn’t figure out how to send custom (not predefined) commands over pronto, and I didn’t want to define all commands from state 0-100 in the code, I switched to raw and it was much easier to work with.

You are right that I could use something like an aircon protocol but my data structure for the raw implementation already includes the adress and checksum :slight_smile: so I’d rather stick to raw as I have more freedom. Also I figured that by building my own specifically for esphome device to device communication, it might be useful to more people as opposed to “hacking”/(mis)using an existing (aircon) protocol.

Currently I’m setting up the data array I transmit over raw in the following manner:

  • The first number in the array will be used as the adress/id of the channel (could be seen as id of the slave but there is nothing that would prevent there being multiple channels between a master and slave device.)
  • The second number will probably end up being used to indicate if this message is a command that requires action and a response, or if it is just a state update.
  • The next numbers in the array will represent target states (if it is a command action), or current states (if it is status update).
  • As the array needs to be an odd length a number (‘333’) is added here if needed to make the array length odd. (Could be any number, but I arbitrarily picked 333 as my code for “blank”).
  • This number is the checksum [ array_sum - (array_sum%2000) ]
  • As I had some issues reading the last value a couple of times (I made a mistake in the setup but figured I’d just leave this) this value is also set to a random number just so the checksum isn’t the last number, in this case it’s ‘333’ (Again could be any number, but I arbitrarily picked 333 as my code for “blank”).

Basically this array is a list of positive non zero integer numbers that represent the pulse lengths of the signal. For sending numbers, I’m not using any fancy encoding to binary, I’m literally just using a pulse length to represent the number value. ie a pulse of length 900 would represent the number 90 etc… Then when i receive 900, i divide by 10 and round to the nearest integer to get the number again to read the state. Has worked well so far.

If you look at the code I shared in my previous post, I made a proof of concept. The state of the number entity is transmitted from pin 16 over a wire that is connected to pin 17. The received data on pin 17 is then interpreted and used to update the sensor entity. This simple test is working very quickly and reliably (so far… :grimacing:)

If you have suggestions, see any potential pitfalls or have ideas for improvements I’d definitely be interested!

This is a really nice repo thank you for sharing! However I would prefer a wired solution to reduce the wireless signals in my house, but this is indeed very similar to what I am looking for. I am currently trying to make something very similar with the remote_transmit and remote_receive protocols that can work both as a wireless implementation and as a wired implementation. Nevertheless this repo will be useful to see how they handle interactions between devices, dropped messages, error handling, etc… Again, thank you for sharing, hadn’t found that one yet :slight_smile:

Makes sense. I guess raw gives you the ultimate flexibility. Interesting how you’re using pulse length like that. Nice idea.

You can dig into my dunny project around here and follow the crumbs to see how I handled sending custom codes in pronto if you feel like it ( zero guarantees I did it well since I’m a novice in this area, and maybe misunderstand your meaning of “custom”).

But well if raw works for you then yeah probably just stick with that.

Thank you for sharing those links, they look interesting! Not sure I’ll have a lot of time today, but I’ll try to find some time soon to have a good look at those :slight_smile:.