So jetzt ist einige Zeit vergangen. Abschließend noch ein paar Dinge der Vollständigkeit halber.
Bezüglich 9.:
Bis heute funktioniert das Einlesen der Temperatur des Thermoelements und die Kommunikation über RS485/Modbus sehr zuverlässig.
Meine aktuellen (& aus meiner Sicht verbesserten) Skripte dazu:
Arduino (Slave):
#include <ModbusRtu.h>
#include "max6675.h"
// Thermoelement
int8_t thermoDO = 4;
int8_t thermoCS = 5;
int8_t thermoCLK = 6;
MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO);
int temp_ddC; //deci degree Celsius
// Modbus
// assign the Arduino pin that must be connected to RE-DE RS485 transceiver
#define TXEN 3
/**
Modbus object declaration
u8id : node id = 0 for master, = 1..247 for slave
port : serial port
u8txenpin : 0 for RS-232 and USB-FTDI
or any pin number > 1 for RS-485
*/
Modbus slave(10, Serial, TXEN); // this is slave @1 and RS-485
// data array for modbus network sharing
const byte AU16DATA_LENGTH = 4;
uint16_t au16data[AU16DATA_LENGTH] = {0, 10, 20, 30}; // uint16_t beim Arduino dasselbe wie unsigned int (max. Wert: 65535 = 6555°C bei centigrad)
// Registerdefinitionen
byte regN_measureEnableFlag = 0;
byte regN_temperature = 1;
byte regN_innerLoopCount = 2; // Testzwecke
byte regN_outerLoopCount = 3;
/////////////////////////////////////////////////////////////////////////
//Setup
/////////////////////////////////////////////////////////////////////////
void setup() {
Serial.begin(19200); // baud-rate at 19200
// Um von Beginn an einen gemessenen Wert im Temperaturregister zu haben (sicherheitshalber)
delay(200);
int i = 0;
temp_ddC = (int)(thermocouple.readCelsius() * 10);
// For the MAX6675 to update, you must delay AT LEAST 250ms between reads!
delay(500);
au16data[regN_temperature] = temp_ddC;
slave.start();
}
/////////////////////////////////////////////////////////////////////////
//Loop
/////////////////////////////////////////////////////////////////////////
void loop() {
//Serial.println("avoidItPlease") --> sehr kritisch!!!, da dadurch auch RX/TX in Gebrauch
//delays vermeiden bzw. nicht verwenden!!!
if (au16data[regN_measureEnableFlag] == 1) { // über Master sicherstellen, dass "AT LEAST 250ms between reads!" liegen
temp_ddC = (int)(thermocouple.readCelsius() * 10); // Messvorgang starten
au16data[regN_measureEnableFlag] = 0; // Reset measureEnableFlag
au16data[regN_temperature] = temp_ddC; // Temperatur in das Temperaturregister schreiben
}
slave.poll(au16data, AU16DATA_LENGTH); //16bit! (register table for communication exchange uint16_t, size of the register table uint8_t)
}
Display More
Raspberry (Master):
#!/usr/bin/env python3
###############################
# blackberry
# used with rs485_06.ino
import os
import minimalmodbus
import serial
import sqlite3
import csv
import time
import datetime as dt
from statistics import median
########################################################################
# # Das sind die Standardwerte, die man aber hier so ändern könnte
# instrument.serial.port # this is the serial port name
# instrument.serial.baudrate = 19200 # Baud
# instrument.serial.bytesize = 8
# instrument.serial.parity = serial.PARITY_NONE
# instrument.serial.stopbits = 1
# instrument.serial.timeout = 0.05 # seconds
# instrument.address # this is the slave address number
# instrument.mode = minimalmodbus.MODE_RTU # rtu or ascii mode
# instrument.clear_buffers_before_each_transaction = True
# # To see which settings you actually are using:
# print(instrument)
# # Use a single script for talking to all your instruments (if connected via the same serial port). Create several instrument objects like:
# instrumentA = minimalmodbus.Instrument('/dev/ttyUSB1', 1)
# instrumentA.serial.baudrate = 9600
# instrumentA.serial.timeout = 0.2
# instrumentA.mode = minimalmodbus.MODE_RTU
# instrumentB = minimalmodbus.Instrument('/dev/ttyUSB1', 2)
# instrumentB.mode = minimalmodbus.MODE_ASCII
# instrumentC = minimalmodbus.Instrument('/dev/ttyUSB2', 1)
# # The instruments sharing the same serial port share the same serial Python object, so instrumentB will have the same baudrate and timeout as instrumentA.
def db_anlegen():
connection = sqlite3.connect(path_db)
cursor = connection.cursor()
# Tabelle erzeugen
sql_statement = 'CREATE TABLE Temperatur_tbl (Zeit DATETIME, Temperatur INTEGER)'
cursor.execute(sql_statement)
connection.commit()
connection.close()
def db_insert_values(time, v_1):
connection = sqlite3.connect(path_db)
cursor = connection.cursor()
cursor.execute('INSERT INTO Temperatur_tbl VALUES (?, ?)', (time, v_1))
connection.commit()
connection.close()
def read_temp(nMedian):
try:
## --> aktuell noch mit ganzen Registern statt: instrument.write_bit(3, 1, 5) #write_bit(registeraddress, value, functioncode=5)
wertelisteTemp = []
for i in range (0, nMedian):
instrument.write_register(regN_measureEnableFlag, 1) #restliche Argumente default: write_register(registeraddress, value, number_of_decimals=0, functioncode=16, signed=False)
time.sleep(time_readAndDelayArduino)
temperature_raw = int(instrument.read_register(regN_temperature, 0)) # Registernumber/registeraddress, number of decimals --> decidegree
wertelisteTemp.append(temperature_raw)
temperature = median(wertelisteTemp)
#print(temperature)
readTempError = False
except IOError as e:
print(e)
print("Fehler Modbus")
reading_time = dt.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
with open('/home/pi/Desktop/Modbus_fail.csv','a') as csv_file:
writer = csv.writer(csv_file)
writer.writerow([reading_time])
temperature = -999 #verbessern
readTempError = True
return readTempError, temperature #as tuples
########################################################################
#vorläufige Main
path_db = '/yourpath/yourDB.db'
temperature = -990 #d°C
temperature_fixpoint = -990 #d°C
minSaveInterval = 2*60 #minutes*seconds/min
maxSaveInterval = 30*60
errorWaitInterval = 5
maxNonSaveSteps = maxSaveInterval // minSaveInterval #Ganzzahldivision
tempDiffSaveLimit = 30 #deci degree celsius
saveCounter = 0
nonSaveStepsLimitPostSaveEn = 1 #sobald Limit überschritten wird nachträglich ein Wert bei hoher Temperaturdifferenz gespeichert zur besseren zeitlichen Lokalisierung
tempDiffSaveLimitPostSaveEn = tempDiffSaveLimit * 3
reading_time_old = 'FatalError_shouldNeverReached'
temperature_old = -990 # kann nie erreicht werden sofern man nicht nachträglich Fehler reinprogrammiert
instrument = minimalmodbus.Instrument('/dev/ttyUSB0', 10) # port name, slave address (in decimal) --> siehe Arduino Modbus slave(x,x,x)
instrument.serial.timeout = 0.4
if not os.path.exists(path_db):
print("Datenbank nicht vorhanden - Datenbank wird anglegt.")
db_anlegen()
print("Skript zur Temperaturmessung und -aufzeichnung gestartet_Version_6")
# Register:
regN_measureEnableFlag = 0
regN_temperature = 1
regN_innerLoopCount = 2
regN_outerLoopCount = 3
time_readAndDelayArduino = 0.6 #Mess- und Verzögerungszeit Arduino
nMedian = 3
while(True):
readTempError, temperature = read_temp(nMedian)
if (not readTempError):
tempDiffActlAbs = abs(temperature-temperature_fixpoint)
reading_time = dt.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
if (tempDiffActlAbs >= tempDiffSaveLimit) or (saveCounter >= maxNonSaveSteps): #10d°C =1°C
# im Falle einer großen Änderung wird der vorherige, übersprungene Wert doch noch gespeichert zur besseren zeitlichen Lokalisierung
if ((tempDiffActlAbs >= tempDiffSaveLimitPostSaveEn) and (saveCounter >= nonSaveStepsLimitPostSaveEn)): #konkret: wenn tempdiff > 9°C und mindestens einmal nicht gespeichert wurde
db_insert_values(reading_time_old, temperature_old)
print("Temperaturwerte nachgetragen_______________")
#neue Werte speichern
db_insert_values(reading_time, temperature)
saveCounter = 0
temperature_fixpoint = temperature # nur beim ersten Mal nach letzter Speicherung speichern = Bezugswert für Abweichung
else:
saveCounter += 1
temperature_old = temperature
reading_time_old = reading_time
time.sleep(minSaveInterval)
else:
time.sleep(errorWaitInterval)
Display More
[Hinweis: Falls Kompilierungsfehler auftreten, dann deshalb, weil ich vor dem Upload noch unnötiges und persönliches entfernt habe.]
Die Funktionsweise in kurz:
Der Arduino schaut ständig mit slave.poll() ob er vom Master angesprochen wird. Wichtig hierbei ist, dass dieser Aufruf nicht blockiert wird (#blinkWithoutDelay...) (Im alten Skript hat das mit delay() zwar funktioniert, das war aber sehr unsauber). Wenn gemessen werden soll, setzt der Master das measureEnableFlag-Register auf TRUE. Die gemessene Temperatur wird daraufhin vom Arduino in das Temperaturregister geschrieben und die Flag zurückgesetzt. Das Temperaturregister liest der Master nach einer kurzen Wartezeit aus und geht dann zur Datenverarbeitung über.
Um die Datenbank nicht sofort vollzumüllen, werden die gemessenen Werte abhängig von der absoluten Änderung bzgl. des zuletzt gespeicherten Werts in variablen Zeitintervallen abgespeichert.
Bezüglich 4.:
Ich habe aktuell noch Probleme mit der zuverlässigen Anbindung eines 2. Slaves, der 10m entfernt ist. Falls ich es nicht bald hinbekomme, wird es dazu evtl. ein neues Thema geben.
Bezüglich 8.:
Antworten sind immer noch willkommen.
Ansonsten würde ich dieses Thema nun als erledigt markieren, mich bedanken und hoffen, dass es in Zukunft dem ein oder anderen noch ein wenig weiterhilft.