Modernization of the Pettix S36 auto feeder

Modernization of the Pettix S36 auto feeder

Project for ESPHome and Home Assistant


Watch video Automatic feeder with bowl on scales

Watch the video Automatic feeder with a rotating bowl on a scale


We untie the feeder from the Tuya cloud and transfer it to ESPHome

Connecting ESP to the feeder

Unsolder the WBR2 chip and connect the ESP WBR2 Module Datasheet




Decoding the protocol for controlling the feeder

Enable slow feed

55:AA:00:06:00:05:06:01:00:01:01:13

Turn off slow feed

55:AA:00:06:00:05:06:01:00:01:00:12

Enable 24 hours

55:AA:00:06:00:05:66:01:00:01:01:73

Turn off 24 hours

55:AA:00:06:00:05:66:01:00:01:00:72

Feed serving

1 serving

55:AA:00:06:00:08:03:02:00:04:00:00:00:01:17

2 servings

55:AA:00:06:00:08:03:02:00:04:00:00:00:02:18

3 servings

55:AA:00:06:00:08:03:02:00:04:00:00:00:03:19

4 servings

55:AA:00:06:00:08:03:02:00:04:00:00:00:04:1A

5 servings

55:AA:00:06:00:08:03:02:00:04:00:00:00:05:1B

6 servings

55:AA:00:06:00:08:03:02:00:04:00:00:00:06:1C

Voice playback time

0

55:AA:00:06:00:08:12:02:00:04:00:00:00:00:25

1

55:AA:00:06:00:08:12:02:00:04:00:00:00:01:26

2

55:AA:00:06:00:08:12:02:00:04:00:00:00:02:27

3

55:AA:00:06:00:08:12:02:00:04:00:00:00:03:28

4

55:AA:00:06:00:08:12:02:00:04:00:00:00:04:29

5

55:AA:00:06:00:08:12:02:00:04:00:00:00:05:2A

6

55:AA:00:06:00:08:12:02:00:04:00:00:00:06:2B

Sensor for the presence of feed in the tank

There is food in the container

55:AA:03:07:00:05:0E:05:00:01:00:22

The container has run out of food

55:AA:03:07:00:05:0E:05:00:01:01:23
ESPHome

View configurations here


Calibrate your scale before using all the code. Remove these lines from the code and enable logging in DEBUG mode. This is how we will receive raw data. Record the weight without a load, copy the numbers from the logs as they are, then take a 500 gram load and put it on the scales, record the numbers. Write all these numbers into a linear filter

An example of a filter, where -169085 is the raw value and this is the value without a weight on the scale, so I indicated that this value has a weight of 0 grams, and the value -92230 was displayed in the logs after I set the weight to 500 grams and then indicated that this value has a weight of 500 grams

filters:
  - calibrate_linear:
      - -169085 -> 0
      - -92230 -> 500

This is what the code looks like with logging in debug mode and without using a linear calibration filter. This will allow you to get the raw values

#Logging
logger:
  level: DEBUG #Debug mode

sensor:
  # Cat bowl scales
  - platform: hx711
    name: "${node_name} Weight"
    icon: mdi:scale
    id: idWeight
    dout_pin: D7 #DT
    clk_pin: D6  #SCK
    gain: 64
    update_interval: 1s
    unit_of_measurement: g
    accuracy_decimals: 0
    device_class: weight
    state_class: measurement
    entity_category: diagnostic
    internal: False

If the readings are unstable, fluctuate frequently and strongly, then replace the HX711 controller with a working one, otherwise the filters will not help. I personally encountered this myself and wasted a lot of time setting up filters. If the HX711 controller is working properly, the scale readings at rest may change slightly and this is normal, but they do not fluctuate greatly and constantly. In this case, an additional filter will help, for example the median filter, which can stabilize the scale readings. Read more in the ESPHome documentation

      - median:
          window_size: 7
          send_every: 4
          send_first_at: 3

Automatic feeder with bowl on scales

What you need to assemble a bowl scale
  • Sensitive strain gauges with 1 gram accuracy. You can find them in electronic kitchen scales with round legs. We take any kitchen scale with round legs, not sticks. This can be easily understood if you turn the scales over. I took these kitchen scales
  • ESP8266 Wemos Mini D1
  • HX711 scale controller
  • Print the platform. You can download here
Photos




Connection diagram and assembly





Home Assistant

For the card to work, you need to install components

Card and templates

  • You can get the card code here
  • The template code can be found here
3D model

The platform was designed in FreeCAD. Download FreeCAD available here. I have included 3 files, two STL files and one for FreeCAD where you can edit if necessary. I designed it so that the strain gauges would hold tightly and made the clips in the form of an arc, which is why the strain gauges hardly fall into place; you need to pry them off with a thin flat-head screwdriver, but they stand clearly and it will be very difficult to remove them without damaging the case.

Ready-made models can be downloaded here

Automatic feeder with rotating bowl with scales

What you need to assemble a rotating bowl with scales
  • Sensitive strain gauges with 1 gram accuracy. You can find them in electronic kitchen scales with round legs. We take any kitchen scale with round legs, not sticks. This can be easily understood if you turn the scales over. I took these kitchen scales

  • ESP8266 Wemos Mini D1

  • HX711 scale controller

  • ULN2003 driver module and 28YBJ 48 stepper motor

  • DS1307 Real Time Clock (RTS) Module

  • Bearing 6814 2RS (61814) SLZ. Took here
    *Outer diameter: 90mm
    *Inner diameter: 70mm
    *Height: 10mm

  • Liquid rubber KUDO COLOR FLEX, Transparent. Took here

  • Print out the bowl and platform. You can download here
Photos







Connection diagram

We take power from the automatic feeder itself, which produces 5V and 1A, which will be enough to power the entire feeder completely. For full operation of the feeder, a current of up to 800mA is required.

Home Assistant

For the card to work, you need to install components

Card and templates

  • You can get the card code here
  • The template code can be found here
3D model

The bowl consists of several parts. Made to save printing time and filament in case the part breaks and to avoid printing the entire bowl again. Read about bearing 6814 2RS (61814) SLZ below

Ready-made models can be downloaded here

The orange circle is bearing 6814 2RS (61814) SLZ, which is inserted into a round platform under a steel bowl (blue in the screenshot). To prevent the steel bowl from sliding on the top of the platform, I recommend spraying it with liquid rubber. Provides excellent grip on the bowl

2 Likes

We are finalizing the autocormer

For future reference, “autocormer” isn’t an English word.

Perhaps what you want to say is “autofeeder”.

1 Like

Thanks for the comment. Fixed

Thanks to this post I learned about an interesting ESP32 board with a compartment for an 18650 battery and this can be used in an auto feeder as a backup power supply in case the electricity goes out, and also monitor the battery charge level. I bought an ESP32 with a battery here

I also made sensors that show not only the battery level, but also the remaining charging time to 100% and discharging to 0%.


I used a divider of two 220 kOhm resistors and lowered the voltage to 2.4 V to fall into the range from 150 to 2450. I took it from the table in the documentation esp32 on ADC voltage.

Next, I measured the voltage with a multimeter at full discharged and charged battery and used a filter, which obtained correct voltage readings with a small error of -/+ 0.05V. The fully measured battery had a voltage of 4.175V, and in the sensor the value was 2.151, this is a figure in parrots and I converted it to the real value thus 4.175/2.151 = 1.940957694095769 ~ 1.941

filters:
      - multiply: 1.941

Working code. If you have your own thoughts on this matter, I will only be glad if you post your version of the code.

#####################################################################################
###################################### Variables ####################################
substitutions:
  name: feeder-pettix-s36

#####################################################################################
################################# Basic configuration ###############################
esphome:
  name: $name
  friendly_name: Feeder Pettix S36
  comment: Feeder Pettix S36

#####################################################################################
####################################### Platform ####################################
esp32:
  board: esp32dev
  framework:
    type: arduino

#####################################################################################
################################ WiFi and access point ##############################
#Wi-Fi credentials for connecting the board to the home network
wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  fast_connect: off
  reboot_timeout: 5min

#If there is no connection with WiFi, then the access point will rise
  ap:
    ssid: ESP Feeder Pettix S36
    password: !secret ap_esp_password
    ap_timeout: 1 min
    manual_ip:
      static_ip: 192.168.4.1
      gateway: 192.168.4.1
      subnet: 255.255.255.0

#The mdns component forces the node to announce itself on the local network using the DNS Multicast Protocol (mDNS), by default for mDNS disabled: false
mdns:
  disabled: false

#The captive portal component in ESPHome is a backup mechanism in case the connection to the configured Wi-Fi fails
captive_portal:

#Web server
web_server:
  port: 80

#Logging
logger:
  baud_rate: 0

#Enable Home Assistant API
api:

#Over-the-Air Update (OTA)
ota:
  password: "esphome"

#####################################################################################
################################## Sensor ###########################################
sensor:
#ADC sensor for battery level sensor
  - platform: adc
    name: ADC
    icon: mdi:flash
    id: idADC
    pin: GPIO32
    attenuation: 11db
    update_interval: 60s
    accuracy_decimals: 3
    internal: false #Hide - true \show - false

#Battery level sensor
  - platform: copy
    name: Battery Level
    icon: mdi:battery
    id: idBatteryLevel
    source_id: idADC
    unit_of_measurement: '%'
    accuracy_decimals: 0
    filters:
      - calibrate_linear:
          #The battery voltage should not be lower than 3V and higher than 4.2V. To get into the range 0-2500 you need to use a divider to reduce the voltage to 2.4V and specify attenuation: 11db
          #A divider of resistors 220 and 220 kom is used
          - 1.545 -> 0
          - 2.151 -> 100

#Battery voltage sensor. The readings are reliable with an error
  - platform: copy
    name: "Battery Voltage"
    icon: mdi:flash
    id: idBatteryVoltage
    source_id: idADC
    accuracy_decimals: 3
    filters:
      - multiply: 1.941


#####################################################################################
##################################### Text sensor ###################################
text_sensor:
#IP
  - platform: wifi_info
    ip_address:
      name: IP
      icon: mdi:ip-network

#ESPHome Version
  - platform: version
    name: "ESPHome Version"
    hide_timestamp: true

#Uptime
  - platform: template
    name: "Uptime"
    icon: mdi:clock-start
    id: idUptimeESP
    entity_category: diagnostic

  - platform: template
    name: "Time"
    icon: mdi:clock-digital
    id: idTime
    update_interval: 10s
    lambda: |-
      auto time_text = id(homeassistant_time).now().strftime("%H:%M:%S / %d-%m-%Y");
      return { time_text };

#Remaining time until charging 100%
  - name: "Charging time up to 100%"
    id: idChargingTime
    platform: template
    update_interval: 10s
    lambda: |-
      auto time = id(homeassistant_time).now();
      if (!time.is_valid())
      {
        return {};
      }
      auto diff = time.timestamp;
      if (id(idBatteryLevel).state != 100)
      {
        auto result = ((diff /  id(idBatteryLevel).state) * (100 - id(idBatteryLevel).state)) / 86400;
        result = result < 0 ? 0 : result;
        auto hours = static_cast<int>(result) / 3600;
        auto minutes = (static_cast<int>(result) % 3600) / 60;
        return to_string(hours) + "h " + to_string(minutes) + "m";
      }
      else
      {
        auto result = diff / 86400;
        result = result < 0 ? 0 : result;
        auto hours = static_cast<int>(result) / 3600;
        auto minutes = (static_cast<int>(result) % 3600) / 60;
        return to_string(hours) + "h " + to_string(minutes) + "m";
      }


#Remaining time until discharge at 0%
  - name: "Discharge time to 0%"
    id: idDischargeTime
    platform: template
    update_interval: 10s
    lambda: |-
      auto time = id(homeassistant_time).now();
      if (!time.is_valid())
      {
        return {};
      }
      auto diff = time.timestamp;
      if (id(idBatteryLevel).state != 0)
      {
        auto result = ((diff / (100 - id(idBatteryLevel).state)) * id(idBatteryLevel).state) / 86400;
        result = result < 0 ? 0 : result;
        auto hours = static_cast<int>(result) / 3600;
        auto minutes = (static_cast<int>(result) % 3600) / 60;
        return to_string(hours) + "h " + to_string(minutes) + "m";
      }
      else
      {
        auto result = diff / 86400;
        result = result < 0 ? 0 : result;
        auto hours = static_cast<int>(result) / 3600;
        auto minutes = (static_cast<int>(result) % 3600) / 60;
        return to_string(hours) + "h " + to_string(minutes) + "m";
      }



#####################################################################################
####################################### Button ######################################
button:
  - platform: restart
    name: "Restart"
    icon: mdi:restart


#####################################################################################
######################################## Time #######################################
time:
  - platform: homeassistant
    id: homeassistant_time

1 Like

I like using two resistors of the same value like this as it simplifies wiring (you don’t have to track which resistor is which).

Nice touches with the time to charge/discharge.

1 Like