FEAT : Anémotrètre en modbus -> mqtt OK :-)
This commit is contained in:
commit
4ca0462d58
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
__pycache__
|
||||||
|
venv/
|
||||||
14
compose.yml
Normal file
14
compose.yml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
esphome:
|
||||||
|
container_name: esphome
|
||||||
|
image: ghcr.io/esphome/esphome:2025.4.2
|
||||||
|
volumes:
|
||||||
|
- "${PWD}/config:/config"
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
restart: always
|
||||||
|
privileged: true
|
||||||
|
network_mode: host
|
||||||
|
environment:
|
||||||
|
- USERNAME=admin
|
||||||
|
- PASSWORD=admin
|
||||||
5
config/.gitignore
vendored
Normal file
5
config/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Gitignore settings for ESPHome
|
||||||
|
# This is an example and may include too much for your use-case.
|
||||||
|
# You can modify this file to suit your needs.
|
||||||
|
/.esphome/
|
||||||
|
/secrets.yaml
|
||||||
48
config/archive/seed-modbus.yaml
Normal file
48
config/archive/seed-modbus.yaml
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
esphome:
|
||||||
|
name: seed-modbus
|
||||||
|
friendly_name: seed modbus
|
||||||
|
|
||||||
|
esp32:
|
||||||
|
board: esp32-s3-devkitc-1
|
||||||
|
framework:
|
||||||
|
type: arduino
|
||||||
|
|
||||||
|
# Enable logging
|
||||||
|
logger:
|
||||||
|
|
||||||
|
# Enable Home Assistant API
|
||||||
|
api:
|
||||||
|
encryption:
|
||||||
|
key: "+fvyzEhRt2I4BPtrS9dqhv/qCibX0azYMgMY1tkEOLw="
|
||||||
|
|
||||||
|
ota:
|
||||||
|
- platform: esphome
|
||||||
|
password: "c3274286d9131444822bed49abf1b77b"
|
||||||
|
|
||||||
|
wifi:
|
||||||
|
ssid: !secret wifi_ssid
|
||||||
|
password: !secret wifi_password
|
||||||
|
|
||||||
|
# Enable fallback hotspot (captive portal) in case wifi connection fails
|
||||||
|
ap:
|
||||||
|
ssid: "Seed-Modbus Fallback Hotspot"
|
||||||
|
password: "j9tvzwj9WPEe"
|
||||||
|
|
||||||
|
captive_portal:
|
||||||
|
|
||||||
|
output:
|
||||||
|
- platform: gpio
|
||||||
|
pin: GPIO21 #GPIO4
|
||||||
|
id: led_output
|
||||||
|
|
||||||
|
switch:
|
||||||
|
- platform: output
|
||||||
|
name: "LED"
|
||||||
|
output: led_output
|
||||||
|
id: led_switch
|
||||||
|
restore_mode: ALWAYS_OFF
|
||||||
|
|
||||||
|
interval:
|
||||||
|
- interval: 1s
|
||||||
|
then:
|
||||||
|
- switch.toggle: led_switch
|
||||||
117
config/seed-modbus.yaml.save
Normal file
117
config/seed-modbus.yaml.save
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
esphome:
|
||||||
|
name: seed-modbus
|
||||||
|
friendly_name: seed modbus
|
||||||
|
on_boot:
|
||||||
|
then:
|
||||||
|
- output.turn_on: output1
|
||||||
|
|
||||||
|
esp32:
|
||||||
|
board: esp32-s3-devkitc-1
|
||||||
|
framework:
|
||||||
|
type: arduino
|
||||||
|
|
||||||
|
# Enable logging
|
||||||
|
logger:
|
||||||
|
level: DEBUG
|
||||||
|
|
||||||
|
# Enable Home Assistant API
|
||||||
|
api:
|
||||||
|
encryption:
|
||||||
|
key: "6wSuBPbrOHBZNl2RG7Dha1yjHddwhVbNK82ET4Pcz3I="
|
||||||
|
|
||||||
|
ota:
|
||||||
|
- platform: esphome
|
||||||
|
password: "6ce9a2a585eb85f9a150d88e6c676483"
|
||||||
|
|
||||||
|
wifi:
|
||||||
|
ssid: !secret wifi_ssid
|
||||||
|
password: !secret wifi_password
|
||||||
|
|
||||||
|
uart:
|
||||||
|
id: uart_bus_a
|
||||||
|
# TX/RX carte :
|
||||||
|
rx_pin: GPIO44
|
||||||
|
tx_pin: GPIO43
|
||||||
|
# RX=D4 -> GPIO5, TX=D5 -> GPIO6
|
||||||
|
# rx_pin: GPIO5
|
||||||
|
# tx_pin: GPIO6
|
||||||
|
# RX=D7, TX=D6
|
||||||
|
baud_rate: 4800
|
||||||
|
|
||||||
|
output:
|
||||||
|
- platform: gpio
|
||||||
|
id: output1
|
||||||
|
pin:
|
||||||
|
number: GPIO3
|
||||||
|
mode:
|
||||||
|
output: True
|
||||||
|
|
||||||
|
modbus:
|
||||||
|
id: modbus1
|
||||||
|
uart_id: uart_bus_a
|
||||||
|
|
||||||
|
modbus_controller:
|
||||||
|
- id: modbus_controller1
|
||||||
|
address: 1
|
||||||
|
modbus_id: modbus1
|
||||||
|
update_interval: 2s
|
||||||
|
|
||||||
|
# select:
|
||||||
|
# - platform: modbus_controller
|
||||||
|
# name: "Modbus Select Register 0"
|
||||||
|
# address: 1
|
||||||
|
# value_type: U_WORD
|
||||||
|
# optionsmap:
|
||||||
|
# "Zero": 0
|
||||||
|
# "One": 1
|
||||||
|
# "Two": 2
|
||||||
|
# "Three": 3
|
||||||
|
|
||||||
|
|
||||||
|
# select:
|
||||||
|
# - platform: modbus_controller
|
||||||
|
# name: "Modbus Select Register 1"
|
||||||
|
# address: 1
|
||||||
|
# value_type: U_WORD
|
||||||
|
# optionsmap:
|
||||||
|
# "Zero": 0
|
||||||
|
# "One": 1
|
||||||
|
# "Two": 2
|
||||||
|
# "Three": 3
|
||||||
|
# lambda: |-
|
||||||
|
# ESP_LOGD("Reg1000", "Received value %lld", x);
|
||||||
|
|
||||||
|
mqtt:
|
||||||
|
broker: "pc-raymond-wifi.home"
|
||||||
|
id: mqtt1
|
||||||
|
log_topic: rbo
|
||||||
|
|
||||||
|
sensor:
|
||||||
|
- platform: modbus_controller
|
||||||
|
id: sensor1
|
||||||
|
modbus_controller_id: modbus_controller1
|
||||||
|
name: "Wind speed"
|
||||||
|
device_class: wind_speed
|
||||||
|
register_type: holding
|
||||||
|
address: 0
|
||||||
|
unit_of_measurement: "m/s"
|
||||||
|
value_type: U_WORD
|
||||||
|
accuracy_decimals: 1
|
||||||
|
filters:
|
||||||
|
- multiply: 0.1
|
||||||
|
- max:
|
||||||
|
window_size: 5
|
||||||
|
send_every: 5
|
||||||
|
- exponential_moving_average:
|
||||||
|
alpha: 0.1
|
||||||
|
send_every: 12
|
||||||
|
- platform: template
|
||||||
|
id: mqtt_wind_speed
|
||||||
|
lambda: |-
|
||||||
|
ESP_LOGD("Reg1000", "Received value %lld", id(sensor1).state);
|
||||||
|
return id(sensor1).state;
|
||||||
|
on_value:
|
||||||
|
then:
|
||||||
|
- mqtt.publish:
|
||||||
|
topic: "home/wind_speed"
|
||||||
|
payload: !lambda 'return to_string(id(mqtt_wind_speed).state);'
|
||||||
116
config/seeed-studio.yaml
Normal file
116
config/seeed-studio.yaml
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
esphome:
|
||||||
|
name: seeed-studio
|
||||||
|
friendly_name: seeed studio
|
||||||
|
on_boot:
|
||||||
|
then:
|
||||||
|
- output.turn_on: DE_RE
|
||||||
|
|
||||||
|
esp32:
|
||||||
|
board: esp32-s3-devkitc-1
|
||||||
|
framework:
|
||||||
|
type: arduino
|
||||||
|
|
||||||
|
# Enable logging
|
||||||
|
logger:
|
||||||
|
|
||||||
|
# Enable Home Assistant API
|
||||||
|
api:
|
||||||
|
encryption:
|
||||||
|
key: "1tRYVfsGzt8mFr+ay9hE24pdPd8IACedduh9tLWg4xs="
|
||||||
|
|
||||||
|
ota:
|
||||||
|
- platform: esphome
|
||||||
|
password: "d68060a1d18b9d61354434595717070c"
|
||||||
|
|
||||||
|
wifi:
|
||||||
|
ssid: !secret wifi_ssid
|
||||||
|
password: !secret wifi_password
|
||||||
|
|
||||||
|
# Enable fallback hotspot (captive portal) in case wifi connection fails
|
||||||
|
ap:
|
||||||
|
ssid: "Seeed-Studio Fallback Hotspot"
|
||||||
|
password: "FrTglhXBIVqi"
|
||||||
|
|
||||||
|
captive_portal:
|
||||||
|
|
||||||
|
output:
|
||||||
|
- platform: gpio
|
||||||
|
pin: GPIO21 # Led de la carte
|
||||||
|
id: led_output
|
||||||
|
- platform: gpio
|
||||||
|
pin: GPIO3 # le pin connecté à DE/RE
|
||||||
|
id: DE_RE
|
||||||
|
|
||||||
|
switch:
|
||||||
|
- platform: output
|
||||||
|
name: "LED"
|
||||||
|
output: led_output
|
||||||
|
id: led_switch
|
||||||
|
restore_mode: ALWAYS_OFF
|
||||||
|
|
||||||
|
interval:
|
||||||
|
- interval: 5s
|
||||||
|
then:
|
||||||
|
- switch.toggle: led_switch
|
||||||
|
- logger.log: "rbo2"
|
||||||
|
|
||||||
|
uart:
|
||||||
|
id: mod_bus_id
|
||||||
|
tx_pin: GPIO44 # Il faut les inverser !!!!
|
||||||
|
rx_pin: GPIO43
|
||||||
|
baud_rate: 4800
|
||||||
|
debug:
|
||||||
|
direction: BOTH
|
||||||
|
dummy_receiver: false
|
||||||
|
after:
|
||||||
|
delimiter: "\n"
|
||||||
|
sequence:
|
||||||
|
- lambda: UARTDebug::log_string(direction, bytes);
|
||||||
|
|
||||||
|
modbus:
|
||||||
|
id: modbus1
|
||||||
|
uart_id: mod_bus_id
|
||||||
|
|
||||||
|
modbus_controller:
|
||||||
|
- id: modbus_controller1
|
||||||
|
address: 1
|
||||||
|
modbus_id: modbus1
|
||||||
|
setup_priority: -10
|
||||||
|
command_throttle: 2s
|
||||||
|
update_interval: 4s
|
||||||
|
|
||||||
|
sensor:
|
||||||
|
- platform: modbus_controller
|
||||||
|
id: sensor1
|
||||||
|
modbus_controller_id: modbus_controller1
|
||||||
|
name: "Wind speed"
|
||||||
|
device_class: wind_speed
|
||||||
|
register_type: holding
|
||||||
|
address: 0
|
||||||
|
unit_of_measurement: "m/s"
|
||||||
|
value_type: U_WORD
|
||||||
|
accuracy_decimals: 1
|
||||||
|
filters:
|
||||||
|
- multiply: 0.1
|
||||||
|
- max:
|
||||||
|
window_size: 1
|
||||||
|
send_every: 1
|
||||||
|
- min:
|
||||||
|
window_size: 1
|
||||||
|
send_every: 1
|
||||||
|
# - exponential_moving_average:
|
||||||
|
# alpha: 0.1
|
||||||
|
# send_every: 12
|
||||||
|
on_value:
|
||||||
|
then:
|
||||||
|
- mqtt.publish:
|
||||||
|
topic: "rbo2"
|
||||||
|
payload: !lambda 'return to_string(x);'
|
||||||
|
|
||||||
|
mqtt:
|
||||||
|
broker: "pc-raymond.home"
|
||||||
|
id: mqtt1
|
||||||
|
log_topic: rbo
|
||||||
|
birth_message:
|
||||||
|
topic: rbo2
|
||||||
|
payload: 'send_every: 1 !'
|
||||||
9
mqtt/compose.yml
Normal file
9
mqtt/compose.yml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
services:
|
||||||
|
mqtt:
|
||||||
|
image: eclipse-mosquitto:2.0
|
||||||
|
restart: unless-stopped
|
||||||
|
# volumes:
|
||||||
|
# - /grab/data/mosquitto:/mosquitto
|
||||||
|
ports:
|
||||||
|
- 1883:1883
|
||||||
|
command: "mosquitto -c /mosquitto-no-auth.conf"
|
||||||
20
notes.md
Normal file
20
notes.md
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Notes
|
||||||
|
|
||||||
|
## Matériel
|
||||||
|
|
||||||
|
### Anémomètre
|
||||||
|
|
||||||
|
SN-3000-FSJT-N01
|
||||||
|
|
||||||
|
```
|
||||||
|
Nom : Lecteur de pare-brise en polycarbonate 485
|
||||||
|
Alimentation : 10~30V
|
||||||
|
Séquence de lignes :
|
||||||
|
Marron : alimentation positive
|
||||||
|
Noir : alimentation négative
|
||||||
|
Jaune (vert) : 485 A
|
||||||
|
Bleu : 485 B
|
||||||
|
Numéro de lot : 2502
|
||||||
|
Propriétaire : Shandong Saien Electronic Technology Co., Ltd.
|
||||||
|
Adresse : n° 1193, route Gangyuan, ville de Jinan, province du Shandong
|
||||||
|
```
|
||||||
58
proto/configModel.py
Normal file
58
proto/configModel.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
from typing import Optional
|
||||||
|
from enum import Enum
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
class Algo(Enum):
|
||||||
|
IEEE2 = "ieee754_sur_2_registres"
|
||||||
|
NORMAL = "lecture_directe"
|
||||||
|
STRING = "Chaîne de caractères"
|
||||||
|
UINT32 = "Entier sur 32 bits non signé"
|
||||||
|
BOOLEAN = "Booléen"
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Metrique:
|
||||||
|
# Id de la métrique pour ce capteur :
|
||||||
|
idMetrique:int
|
||||||
|
indexRegistreDepart:int
|
||||||
|
indexRegistreFin:int = None # Ne sert que dans le cas de Algo.STRING
|
||||||
|
# Nombre de chiffres après la virgule (utilisé si Algo.IEEE2)
|
||||||
|
precision:int = 2
|
||||||
|
algo:Algo = Algo.NORMAL
|
||||||
|
|
||||||
|
class ModbusType(Enum):
|
||||||
|
RTU = "rtu"
|
||||||
|
TCP = "tcp"
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class RtuConfig:
|
||||||
|
baudrate:int
|
||||||
|
stopbits:int
|
||||||
|
bytesize:int
|
||||||
|
parity:str
|
||||||
|
serialPort:str = "/dev/ttyACM0"
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class TcpConfig:
|
||||||
|
ip:str
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AppelCapteur:
|
||||||
|
firstRegisterAdress:int
|
||||||
|
nbRegisters:int
|
||||||
|
# Les métriques associées à ce capteur
|
||||||
|
metriques:list[Metrique]
|
||||||
|
# Le délais, en secondes, entre 2 appels (et stockage en bdd des métriques liées) avec 15s comme valeur par défaut :
|
||||||
|
delai:int = 15
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Capteur:
|
||||||
|
idCapteur:int
|
||||||
|
modbusAdresse:int
|
||||||
|
appels:list[AppelCapteur]
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Canal:
|
||||||
|
description:str
|
||||||
|
typeModbus:ModbusType
|
||||||
|
comConfig:RtuConfig|TcpConfig
|
||||||
|
capteurs:list[Capteur]
|
||||||
7
proto/requirements.txt
Normal file
7
proto/requirements.txt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
APScheduler==3.10.4
|
||||||
|
psycopg2-binary==2.9.10
|
||||||
|
pymodbus==3.7.0
|
||||||
|
pyserial==3.5
|
||||||
|
pytz==2024.1
|
||||||
|
six==1.16.0
|
||||||
|
tzlocal==5.2
|
||||||
60
proto/t.py
Executable file
60
proto/t.py
Executable file
@ -0,0 +1,60 @@
|
|||||||
|
#!python
|
||||||
|
from pymodbus.client import ModbusSerialClient, ModbusTcpClient
|
||||||
|
from pymodbus.pdu.register_read_message import ReadInputRegistersResponse
|
||||||
|
from configModel import Algo, Metrique, Capteur, ModbusType
|
||||||
|
import struct, time
|
||||||
|
|
||||||
|
# client = ModbusTcpClient(host="shellyproem50-08f9e0e79718") # grarage (borne et pac)
|
||||||
|
# client = ModbusTcpClient(host="ShellyPro3EM-FCE8C0D97664") # bureau (prises 1 à 3)
|
||||||
|
client = ModbusSerialClient(
|
||||||
|
port="/dev/ttyACM0",
|
||||||
|
baudrate=4800,
|
||||||
|
# stopbits=capteur.comConfig.stopbits,
|
||||||
|
# bytesize=capteur.comConfig.bytesize,
|
||||||
|
# parity=capteur.comConfig.parity
|
||||||
|
)
|
||||||
|
|
||||||
|
def ieee754(registres:list):
|
||||||
|
float_bytes = struct.pack('HH', registres[0], registres[1])
|
||||||
|
return struct.unpack('f', float_bytes)[0]
|
||||||
|
|
||||||
|
def uint32(a, b):
|
||||||
|
ret:int = (a << 16) | b
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def lireMetrique(metrique:Metrique, registre):
|
||||||
|
client.connect()
|
||||||
|
try:
|
||||||
|
data:ReadInputRegistersResponse = client.read_input_registers(registre, 2, slave=1)
|
||||||
|
if data:
|
||||||
|
print(f"--> {data}")
|
||||||
|
registres = data.registers
|
||||||
|
if metrique.algo == Algo.NORMAL:
|
||||||
|
val = registres[0]
|
||||||
|
if metrique.algo == Algo.IEEE2:
|
||||||
|
val = round(ieee754(registres[0:2]), metrique.precision)
|
||||||
|
if metrique.algo == Algo.UINT32:
|
||||||
|
val = uint32(registres[0], registres[1])
|
||||||
|
if metrique.algo == Algo.STRING:
|
||||||
|
val = ''.join(chr(register) for register in data.registers)
|
||||||
|
if metrique.algo == Algo.BOOLEAN:
|
||||||
|
val = registres[0]
|
||||||
|
print(f"Captation sur {registre} : {val}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Erreur lors de la captation sur {registre} : {e}")
|
||||||
|
finally:
|
||||||
|
client.close()
|
||||||
|
|
||||||
|
registre = -1
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
while True:
|
||||||
|
registre = 0
|
||||||
|
m:Metrique = Metrique(
|
||||||
|
idMetrique=1,
|
||||||
|
indexRegistreDepart=registre,
|
||||||
|
precision=3,
|
||||||
|
algo=Algo.NORMAL
|
||||||
|
)
|
||||||
|
lireMetrique(m, registre)
|
||||||
|
time.sleep(1)
|
||||||
Loading…
Reference in New Issue
Block a user