FreeOpcUa / python-opcua

LGPL Pure Python OPC-UA Client and Server
http://freeopcua.github.io/
GNU Lesser General Public License v3.0
1.36k stars 658 forks source link

Create continuous cycle of sensor temperature reading #670

Open smalhao opened 6 years ago

smalhao commented 6 years ago

I have an opcua server where a BMP180 sensor is attached, the idea is to read continuously the variable of temperature, altitude, pressure and sea level pressure, I am having some difficulty in creating the continuous cliclo in order to carry out this continuous reading and in which I can access with the Unified Automation UA Expert client, my code is as follows:

import sys
sys.path.insert(0, "..")
import time
import datetime
import subprocess
import logging

from opcua import Server
import BMP085

bmp =BMP085.BMP085()

temperature =bmp.read_temperature()
altitude = bmp.read_altitude()
pressure = bmp.read_pressure()
sealevel_pressure = bmp.read_sealevel_pressure()

##
if __name__ == "__main__":
    logging.basicConfig(level = logging.WARNING)

# get Objects node, this is where we should put our nodes
# setup our server
server = Server()
server.set_endpoint("opc.tcp://192.168.1.10:4840/freeopcua/server/")

# setup our own namespace, not really necessary but should as spec
uri = "http://examples.freeopcua.github.io"
idx = server.register_namespace(uri)

# get Objects node, this is where we should put our nodes
objects = server.get_objects_node()

# populating our address space
sensordata = objects.add_object(idx, "Sensordata")
bmp = sensordata.add_variable(idx, "Temperature", temperature)
bmp = sensordata.add_variable(idx, "Altitude", altitude)
bmp = sensordata.add_variable(idx, "Pressure", pressure)
bmp = sensordata.add_variable(idx, "Sealevel Pressure", sealevel_pressure)
bmp.set_writable()    # Set MyVariable to be writable by clients

# starting!
server.start()

try:
    count = 0

    while True:
        #run script in python of BMP085
       bmp = subprocess.call(" python BMP085.py 1", shell=True)

finally:
    #close connection, remove subcsriptions, etc
    server.stop()
zerox1212 commented 6 years ago

Your code has issues.

  1. You create 4 variable nodes, but you keep overwriting bmp so you only have access to the last node, but then you overwrite bmp again with a subprocess.
  2. You are not updating the variable nodes with node.set_value(val).

You need something like this:

temp_var_node = sensordata.add_variable(idx, "Temperature", temperature)
alt_var_node = sensordata.add_variable(idx, "Altitude", altitude)
press_var_node = sensordata.add_variable(idx, "Pressure", pressure)
sealvl_var_node = sensordata.add_variable(idx, "Sealevel Pressure", sealevel_pressure)

    while True:
        #run script in python of BMP085
       bmp = subprocess.call(" python BMP085.py 1", shell=True)
       temp_var_node.set_value(bmp.read_temperature())

I also don't think you need a subprocess here at all (over complicated). At most you could put your BMP code in a thread if it's cyclic, but even that is probably not needed for this simple example.

You have to remember that a OPC UA objects and Python objects are not the same. https://github.com/FreeOpcUa/python-opcua/blob/master/examples/server-ua-python-mirror.py is a good example for a nice way to do what you want. Which is to mirror your BMP device on OPC UA.

smalhao commented 6 years ago

thanks for your help, I removed the subprocess and it worked as I intended, through UaExpert I could prove that it worked well. Just another question, how can I send the data to another raspberry pi through TCP / IP (Ethernet network)? I don't understood the question of the mirror, can you explain this better?

zerox1212 commented 6 years ago

Just run the client on your other Pi and subscribe to the nodes.

smalhao commented 6 years ago

The client can be a sensor BMP180 attached in a raspberry pi with the script client_example.py and send the data to raspberry pi where is the code of server_example.py? how write the code to establish the communication in client to send data to the server for later access in UaExpert?

zerox1212 commented 6 years ago

I don't understand your plan. You can just make the Pi with the BMP180 be an OPC UA server and enable history. Then any client (Pi or UaExpert) can connect to your server and get live data or read the history.

smalhao commented 6 years ago

My idea is this, I send the data from the sensors via ethernet TCP / IP and access with UaExpert, but I do not know if this is possible as the image shows, ie having a raspberry pi that receives the publications of the sensors data ( publishers) and the client access the data (subscriver).

image

destogl commented 6 years ago

The correct OPC UA architecute would be to make the RPi with sensors a servers. You also do not need 3 RPi, you can have two od them and when one is acting as a server.

This is all explained in example code. and as @oroulet said you need history (see: examples)

smalhao commented 6 years ago

Thanks for your explanation destogl, I think you refer to this diagram as I show in this image, directly do the data acquisition of the sensors variables (temperature, pressure, altitude, etc ...) inside the server_example.py script correct?

image

destogl commented 6 years ago

Yes. This would be other approach

smalhao commented 6 years ago

when i run the sever_example.py in raspberry with the sensor TSL2561, i have this error only when i run the script with the variable lux activated, if i run the script only with the luminosity variable i can observe the values of luminosity variable in the UaExpert.

image

my code is this:

import sys
sys.path.insert(0, "..")
import time
import datetime
import subprocess
import logging

from opcua import Server
import TSL2561

##### Sensor Data of TSL2561 ######################
tsl =TSL2561.TSL2561()
luminosity = tsl.read_raw_luminosity()
lux = tsl.read_lux()
################################################                                                                                 

if __name__ == "__main__":
    logging.basicConfig(level = logging.WARNING)

# get Objects node, this is where we should put our nodes
# setup our server
server = Server()
server.set_endpoint("opc.tcp://192.168.1.11:4840/freeopcua/server/")

# setup our own namespace, not really necessary but should as spec
uri = "http://examples.freeopcua.github.io"
idx = server.register_namespace(uri)

# get Objects node, this is where we should put our nodes
objects = server.get_objects_node()

# populating our address space
########################### Object ######################################
sensordata = objects.add_object(idx, "Sensor Data")
##########################################################################
################# Variables of Object ###################################
luminosity_var_node = sensordata.add_variable(idx, "Luminosity", luminosity)
lux_var_node = sensordata.add_variable(idx, "Lux", lux)
##########################################################################
luminosity_var_node.set_writable()    # Set MyVariable to be writable by clients
lux_var_node.set_writable()
# starting!
server.start()

try:

    while True:
       luminosity_var_node.set_value(tsl.read_raw_luminosity())
       lux_var_node.set_value(tsl.read_lux())

finally:
    #close connection, remove subcsriptions, etc
    server.stop()
zerox1212 commented 6 years ago

The problem isn't OPC UA. Your TSL library has a bug. You can't bitshift a floating point number.

Do something like this: ratio = (int(ratio1) + 1) >> 1

smalhao commented 6 years ago

Thanks zerox1212, is working

smalhao commented 6 years ago

Where i can search an example to create alarms and events, for example if temperature pass a value should be ativate an event for down the temperature

smalhao commented 6 years ago

How can I send and save the data in a database?

zerox1212 commented 6 years ago

Alarms are not supported. You can make basic events if you want, there are examples for that. To enable history you should look at the server history example.

smalhao commented 6 years ago

zerox1212 can you give me a link for i see one example to create basic events?

smalhao commented 6 years ago

zerox1212 can you give me a link for i see one example to create basic events? How i can send data acquired in raspberry to a pc with sqlite database?

zerox1212 commented 6 years ago

The examples are in the examples folder. I can't tell you how to do those things. If you try to write your own code and post it here I might have some time to review it. It's python, you can just try it and it usually works!

smalhao commented 6 years ago

When I try to connect and send de data of TSL2561 to the address of the laptop where the MyQSL database is installed, I get this error:

Traceback (most recent call last):
  File "server_example_TSL2561.py", line 19, in <module>
    db = MySQLdb.connect(host ='192.168.1.15',port='3306',user='root',password='admin',database='OPCUA')
  File "/usr/local/lib/python3.5/dist-packages/MySQLdb/__init__.py", line 85, in Connect
    return Connection(*args, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/MySQLdb/connections.py", line 204, in __init__
    super(Connection, self).__init__(*args, **kwargs2)
TypeError: an integer is required (got type str)

NETWORK and Code

My network

image

My code is:

import sys
sys.path.insert(0, "..")
import time
import datetime
import subprocess
import logging

from opcua import Server
import TSL2561
import MySQLdb

##### Sensor Data of TSL2561 Parameters ######################
tsl =TSL2561.TSL2561()
luminosity = tsl.read_raw_luminosity()
lux = tsl.read_lux()
################################################                                                                                 

db = MySQLdb.connect(host ='192.168.1.15',port='3306',user='root',password='admin',database='OPCUA')
cursor = db.cursor()

if __name__ == "__main__":
    logging.basicConfig(level = logging.WARNING)

# setup our server
server = Server()
server.set_endpoint("opc.tcp://192.168.1.11:4840/freeopcua/server/")

# setup our own namespace, not really necessary but should as spec
uri = "http://examples.freeopcua.github.io"
idx = server.register_namespace(uri)

# get Objects node, this is where we should put our nodes
objects = server.get_objects_node()

# populating our address space
########################### Object ######################################
sensordata = objects.add_object(idx, "Sensor TSL2561")

################# Variables of Object ###################################
luminosity_var_node = sensordata.add_variable(idx, "Luminosity", luminosity)
lux_var_node = sensordata.add_variable(idx, "Lux", lux)
##########################################################################

luminosity_var_node.set_writable()  # Set MyVariable to be writable by clients
lux_var_node.set_writable()

# starting server!
server.start()

try:

    while True:

       luminosity_var_node.set_value(tsl.read_raw_luminosity())
       time.sleep(2)
       lux_var_node.set_value(tsl.read_lux())
       time.sleep(2)
       sql = "INSERT INTO OPCUA values (luminosity, lux)"
       cursor.execute(sql)
       db.commit()

finally:
    #close connection, remove subcsriptions, etc
    server.stop()
    db.close()

I need some help to resolve this.

zerox1212 commented 6 years ago

port='3306'

Look at the SQL docs. Port is probably supposed to be an integer, you are supplying a string....

smalhao commented 6 years ago

I tried to send the luminosity and lux values to the database this way as I have in the following code:

import sys
sys.path.insert(0, "..")
import time
import datetime
import subprocess
import logging

from opcua import Server
import TSL2561
import MySQLdb

##### Sensor Data of TSL2561 Parameters ######################
tsl =TSL2561.TSL2561()
luminosity = tsl.read_raw_luminosity()
lux = tsl.read_lux()
################################################                                                                                 

db = MySQLdb.connect(host ='192.168.1.15',port='3306',user='root',password='admin',database='OPCUA')
cursor = db.cursor()

if __name__ == "__main__":
    logging.basicConfig(level = logging.WARNING)

# setup our server
server = Server()
server.set_endpoint("opc.tcp://192.168.1.11:4840/freeopcua/server/")

# setup our own namespace, not really necessary but should as spec
uri = "http://examples.freeopcua.github.io"
idx = server.register_namespace(uri)

# get Objects node, this is where we should put our nodes
objects = server.get_objects_node()

# populating our address space
########################### Object ######################################
sensordata = objects.add_object(idx, "Sensor TSL2561")

################# Variables of Object ###################################
luminosity_var_node = sensordata.add_variable(idx, "Luminosity", luminosity)
lux_var_node = sensordata.add_variable(idx, "Lux", lux)
##########################################################################

luminosity_var_node.set_writable()  # Set MyVariable to be writable by clients
lux_var_node.set_writable()

# starting server!
server.start()

try:

    while True:

       luminosity_var_node.set_value(tsl.read_raw_luminosity())
       time.sleep(2)
       lux_var_node.set_value(tsl.read_lux())
       time.sleep(2)
       sql = "INSERT INTO OPCUA.OPCUA_SERVER_RASP3(Lux, Luminosity) values (lux, luminosity)"
       #database is OPCUA and the table is OPCUA_SERVER_RASP3
       cursor.execute(sql)
       db.commit()

finally:
    #close connection, remove subcsriptions, etc
    server.stop()
    db.close()

in the database I have these values: image

but I think they are not the correct values because I have a light pointed at the sensor, so I have doubts on the variable that I have to send to the database, can you tell me if with this code the database is receiving the data? I want to send the values of lux and luminosity variables to the database

zerox1212 commented 6 years ago

The variables lux, luminosity are not being updated continuously in your while loop. You need to update those values in your loop or you have to use the values you are storing in your OPC UA variable nodes.

smalhao commented 6 years ago

That variables (luminosity_var_node and lux_var_node) you talk about to use? who i can update the variables lux and luminosity in while loop? i tried but gives me the same value when i increase the temperature

zerox1212 commented 6 years ago

You should probably ask these questions on Stack Overflow. What you are doing has nothing to do with OPC UA.

while True:
    luminosity = tsl.read_raw_luminosity()
    time.sleep(2)
    lux = tsl.read_lux()
    time.sleep(2)

    sql = "INSERT INTO OPCUA.OPCUA_SERVER_RASP3(Lux, Luminosity) values (lux, luminosity)"
    #database is OPCUA and the table is OPCUA_SERVER_RASP3
    cursor.execute(sql)
    db.commit()

If you still have issues try asking in Stack Overflow.

smalhao commented 6 years ago

In the UaExpert Client I see the values of my BMP180 sensor (the raspberry pi where the TSL2561 sensor stopped working and had to start working on the BMP180 sensor).

image

with this code i send values to Database, but not the values of nodes (temp_var_node, press_var_node and altit_var_node) but the values of temperature, pressure and altitude.

import sys
sys.path.insert(0, "..")
import time
import datetime
import subprocess
import logging

from opcua import Server
import BMP085
import mysql.connector as mysql

##### Sensor Data of BMP180 ######################
bmp = BMP085.BMP085()
temperature =bmp.read_temperature()
altitude = bmp.read_altitude()
pressure = bmp.read_pressure()
################################################

if __name__ == "__main__":
    logging.basicConfig(level = logging.WARNING)

# get Objects node, this is where we should put our nodes
# setup our server
server = Server()
server.set_endpoint("opc.tcp://192.168.1.10:4840/freeopcua/server/")

# setup our own namespace, not really necessary but should as spec
uri = "http://192.168.1.10:4840/freeopcua/server/"
idx = server.register_namespace(uri)

# get Objects node, this is where we should put our nodes
objects = server.get_objects_node()

# populating our address space
################################### Object ####################################################
sensordata = objects.add_object(idx, "Sensor One BMP180")
############################# Variables of Object Ua Expert #############################################
temp_var_node= sensordata.add_variable(idx, "Temperature Sensor One", temperature)
print("the temp_var_node is:", temp_var_node)
altit_var_node = sensordata.add_variable(idx, "Altitude Sensor One", altitude)
print("the altit_var_node is:", altit_var_node)
press_var_node = sensordata.add_variable(idx, "Pressure Sensor One", pressure)
print("the pressure_var_node is:", press_var_node)
#################### Set MyVariable to be writable by clients #################################

temp_var_node.set_writable()# Set MyVariable to be writable by clients
altit_var_node.set_writable()
press_var_node.set_writable()

######################### Sending data values of variables to the database ####################################

db = mysql.connect(host = '192.168.1.15',port = 3306,user = 'root',password = 'admin',database = 'OPCUA')
cursor = db.cursor()
delete = "DROP TABLE sensorBMP180_raspberrypi2"
cursor.execute(delete)

sql = """CREATE TABLE sensorBMP180_raspberrypi2 (
         temperature DOUBLE,
         altitude INT,
         pressure INT)"""
cursor.execute(sql)

# starting de server OPCUA!
server.start()
try:

    while True:
 ############## Values in Loop UaExpert ###########################################     
        temp_var_node.set_value(bmp.read_temperature())
        time.sleep(2)   
        altit_var_node.set_value(bmp.read_altitude())
        time.sleep(2)
        press_var_node.set_value(bmp.read_pressure())
        time.sleep(2)
################## Values in Loop to DataBase #########################################
        temperature = bmp.read_temperature()
        pressure = bmp.read_pressure()
        altitude = bmp.read_altitude()
        time.sleep(2)
        cursor.execute("""INSERT INTO sensorBMP180_raspberrypi2 VALUES (%s, %s, %s)""",(temperature,pressure,altitude))
        db.commit()

finally:

    #close connection, remove subcsriptions, etc
    server.stop()
    db.close()
#################################################################################

In database (is in Portuguese) i have this values image