.. currentmodule:: setupdriver .. _setupdriver: Make Your Own Sensor Driver =========================== Using the Data Logger Application provided by Observator you can easily integrate your own sensor (driver). Please study the manual on how to use the OMC-048 Data Logger Application before you start integrating your own sensor driver. Creating a .py driver file -------------------------- Create a new .py file and place this in the \script\modules folder of your OMC-048 data logger. In this example we create the file 'simple_driver.py' but you are free to choose any name you like, as long as there are no duplicate names. Now paste the following code in your driver file: .. code-block:: python :caption: simple_driver.py :linenos: from sensor_drv import SensorDrv class simple_driver(SensorDrv): def __init__(self, serial, conf): SensorDrv.__init__(self) async def read_values(self): self.data = [0] return True async def detect_parameters(self, tag_number = ''): self.parameters = [('parameter', '', 'TAG{}'.format(tag_number))] return True async def flush(self): pass .. important:: The class name (line 3) must be identical to the .py file name! Add your own driver code ------------------------ In this example we are simulating a sensor which transmits data over RS422 on serial port 1 on 1 Hz interval. - serial port 1 - 9600 baud, 8N1 The string transmitted by the sensor is: .. code-block:: python Temp=20.34,Hum=56.4,Status=OK\r\n In our example we want to capture three fields of data: ``Temperature``, ``Humidity`` and ``Status`` We start by adding the parameters to the driver: name, unit and unique tag. We must also provide 3 value's to 'self.data' in the 'read_values' function, corresponding to the given parameters. For now we just enter the decimal values 1, 2 and 3. .. code-block:: python :caption: simple_driver.py :linenos: from sensor_drv import SensorDrv class simple_driver(SensorDrv): def __init__(self, serial, conf): SensorDrv.__init__(self) async def read_values(self): self.data = [1,2,3] return True async def detect_parameters(self, tag_number = ''): self.parameters = [ ('Temperature', '', 'TMP{}'.format(tag_number)), ('Humidity', '', 'HUM{}'.format(tag_number)), ('Status', '', 'STS{}'.format(tag_number)) ] return True async def flush(self): pass Test your driver ---------------- The base for our sensor driver is now provided, we can already test it by adding our sensor driver to the config.txt The entire config is displayed below, note the 'simple_driver' for our example. .. code-block:: yaml :emphasize-lines: 13-18 # ----System---- # Omc048: system_id: PROTO_1 application: PROTO_1 file_log_level: info repl_log_level: info utc_time_offset_hours: +0 utc_time_offset_minutes: +0 sensor_data_print: True usb_mode: debug self_test: False simple_driver: - id: simple_1 sample_interval: "* * * * *" port: serial1 mode: RS422 baudrate: 9600 # ----Data-log-settings---- # Data_file: - id: data create_interval: "0 0,5,10,15,20,25,30,35,40,45,50,55 * * *" Using the REPL to start our application shows our driver is working: .. code-block:: console :emphasize-lines: 12-17 2022-06-10 10:00:02 [APPLICATION] INFO config.txt detected 2022-06-10 10:00:02 [APPHELPER] INFO DEBUG mode is active 2022-06-10 10:00:02 [APPLICATION] INFO UTC time device offset: 0(hrs):0(min) 2022-06-10 10:00:02 [DRIVER_MANAGER] INFO Acquiring parameters simple_driver sensor: simple_1 2022-06-10 10:00:02 [DRIVER_MANAGER] INFO simple_driver sensor detected: simple_1 2022-06-10 10:00:02 [LOGGER_MANAGER] INFO Log parameters appended: simple_1 2022-06-10 10:00:02 [APPLICATION] INFO Application started: PROTO_1 2022-06-10 10:00:02 [APPLICATION] INFO File Syslog level: info 2022-06-10 10:00:02 [APPLICATION] INFO Repl Syslog level: info 2022-06-10 10:00:02 [DATALOG045] INFO Started logging in 'data/OMC-045_PROTO_1_048000201_220610_100002.txt' 2022-06-10 10:00:02 [SCHEDULER] INFO Enabled sleeping in between tasks 2022-06-10 10:00:03 [DRIVER_MANAGER] INFO Sensor [simple_1] power on 2022-06-10 10:00:03 [DRIVER_MANAGER] INFO Sensor [simple_1] data successfully obtained 2022-06-10 10:00:03 [DRIVER_MANAGER] INFO Sensor [simple_1] power off Sensor data: ('Temperature', '', 'TMP_simple_1') 1 Sensor data: ('Humidity', '', 'HUM_simple_1') 2 Sensor data: ('Status', '', 'STS_simple_1') 3 2022-06-10 10:00:03 [SCHEDULER] INFO Sleep for 0.749 seconds 2022-06-10 10:00:04 [DRIVER_MANAGER] INFO Sensor [simple_1] power on 2022-06-10 10:00:04 [DRIVER_MANAGER] INFO Sensor [simple_1] data successfully obtained 2022-06-10 10:00:04 [DRIVER_MANAGER] INFO Sensor [simple_1] power off Sensor data: ('Temperature', '', 'TMP_simple_1') 1 Sensor data: ('Humidity', '', 'HUM_simple_1') 2 Sensor data: ('Status', '', 'STS_simple_1') 3 .. note:: The base of our sensor is operational now, from this point we can start adding the actual sensor driver by reading the data string over the given serial port. Simulating the data ------------------- We now need to simulate the data, this can be done using a generic serial talker or use another OMC-048 using the script below: :: import omc048, obs print("simple_driver sensor simulator") s2 = omc048.serial(2) s2.init(9600, s2.RS422, txbuf=1024) sensor_data = "Temp=20.34,Hum=56.4,Status=OK\r\n" while True: s2.write(sensor_data) print(sensor_data) obs.delay(1000) Connect the simulated sensor output to the OMC-048 data logger and edit the simple_driver file to the example below: .. code-block:: python :caption: simple_driver.py :linenos: from sensor_drv import SensorDrv from read_line import Read_line class simple_driver(SensorDrv): def __init__(self, serial, conf): SensorDrv.__init__(self) self.__serial_data = Read_line(serial) async def read_values(self): while True: received = await self.__serial_data.update() if received: print('received data:', received) data = received[0].decode() data_replace = data.replace('\r\n','') data_replace = data_replace.replace('=',',') data_split = data_replace.split(',') print('split data:', data_split) break self.data = [data_split[1],data_split[3],data_split[5]] return True async def detect_parameters(self, tag_number = ''): self.parameters = [ ('Temperature', '', 'TMP{}'.format(tag_number)), ('Humidity', '', 'HUM{}'.format(tag_number)), ('Status', '', 'STS{}'.format(tag_number)) ] return True async def flush(self): pass .. note:: We have used the read_line module to collect the data. Prints were added on the original input data as wel as the split data to show the results Running the application presents us the following: .. code-block:: console :emphasize-lines: 1-2 received data: [b'Temp=20.34,Hum=56.4,Status=OK\r\n'] split data: ['Temp', '20.34', 'Hum', '56.4', 'Status', 'OK'] 2022-06-10 10:52:14 [DRIVER_MANAGER] INFO Sensor [simple_1] data successfully obtained Sensor data: ('Temperature', '', 'TMP_simple_1') 20.34 Sensor data: ('Humidity', '', 'HUM_simple_1') 56.4 Sensor data: ('Status', '', 'STS_simple_1') OK This concludes our simple driver example. Your own sensor driver now workes as any other! The data is automatically added to the logs! .. important:: - The 'detect_parameters' is executed on startup and should actually check for a present sensor, this prevents the sensor from being scheduled if not attatched. - The 'read_values' is executed every given cron interval and should only return 'True' in case the sensor reading was succesfully obtained and 'self.data' filled with data. - You can add sensor specific settings to the config.txt file by using the config module! - There are many sensor drivers you can use as an example, you can find them in the 'script/modules' folder .. tip:: Can't get it figured out? Feel free to contact us!