arraiz
Hello first want to tell that I think OpenPLC is a great tool to learn about "OT" Hardware and communications and very easy to set up, so keep going!.

Recently I have purchased a DS18B20 temperature sensor it works via OneWire protocol as a digital input. I am just wondering if it is posible to get it to work with OpenPLC running on my Raspberry PI, and make a silly program that turns a led to high when certain value is raised (for example).

Thanks is advance!
Quote 0 0
thiagoralves
Hi arraiz, welcome to the forum. Just search for DS18B20 here on the forum and you will get a couple answers.
Quote 0 0
arraiz
Thanks Thiago I will.
Quote 0 0
arraiz

Hello again, searching in the forum I just discovered a post by @Stensig talking about that but i have some questions for the implementation.

1º OpenPLC has the "Hardware Layer Code Box" that can be used to add the 1-wire sensors to the RPI right ?, so then I can read the sensor using the HWLCB  

2º I have the basic schema in Ladder Logic (never programmed a PLC before, sorry if its wrong). Its simple if the temperature its greater than a value the led pop-ups.

Screenshot 2020-04-14 at 19.48.26.png 
I think I have understand that I need to read the sensor from the Hardware layer code box, overriding the input for the temperature (Temp_Sensor) in the Ladder Logic in the image.

Can all the program be implemented in the HWLCB?.

Unfortunately my C is not good enough to do this without a little help, and I don't know where to star, Im new to this.

First I need to declare the DS18B20 like this rigth?

int ignored_int_inputs[] ={2} //for the input of the sensor in the %IX0.2


but then? how I read the sensor ? is there any documentation to do this?


Many Many thanks.








Quote 0 0
thiagoralves
This is not a standard feature in OpenPLC, and that's why it might get a little complicated if you don't know C or C++. The purpose of the HWLCB (I liked the acronym) is for the user to develop features that are not built in. You will have to find a C code that works on the Raspberry Pi and that can read this sensor that you are looking for. A quick google search brought me some examples that might work. Once you have the code working in your Pi, post it here and I can help you integrate that code on HWLCB.
Quote 0 0
arraiz
Thank you very much Thiago.
I found this little C script that works well in Github

#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
    DIR *dir;
    struct dirent *dirent;
    char dev[16];      
    char devPath[128]; 
    char buf[256];     
    char tmpData[6];   
    char path[] = "/sys/bus/w1/devices";
    ssize_t numRead;
    dir = opendir(path);
    if (dir != NULL)
    {
        while ((dirent = readdir(dir)))
            // 1-wire devices are links beginning with 28-
            if (dirent->d_type == DT_LNK && strstr(dirent->d_name, "28-") != NULL)
            {
                strcpy(dev, dirent->d_name);
                printf("\nDevice: %s\n", dev);
            }
        (void)closedir(dir);
    }
    else
    {
        perror("Couldn't open the w1 devices directory");
        return 1;
    }
    // Assemble path to OneWire device
    sprintf(devPath, "%s/%s/w1_slave", path, dev);
    // Read temp continuously
    // Opening the device's file triggers new reading
    while (1)
    {
        int fd = open(devPath, O_RDONLY);
        if (fd == -1)
        {
            perror("Couldn't open the w1 device.");
            return 1;
        }
        while ((numRead = read(fd, buf, 256)) > 0)
        {
            strncpy(tmpData, strstr(buf, "t=") + 2, 5);
            float tempC = strtof(tmpData, NULL);
            printf("Device: %s  - ", dev);
            printf("Temp: %.3f C  ", tempC / 1000);
            printf("%.3f F\n\n", (tempC / 1000) * 9 / 5 + 32);
        }
        close(fd);
    }
    /* return 0; --never called due to loop */
}



with this script y can get the value of the sensor 
Quote 0 0
thiagoralves
Great! Is the temperature output correct, like, if you put the sensor in ice do you see an immediate drop in temperature readings? If so, I can incorporate this code on a HWLCB quickly
Quote 0 0
arraiz

I tested with a hand lighter, putting the flame close to the sensor (just a bit...) and the temp was rising good I think, then, in cold water values dropped realtively fast, all values are close to reality:

22ºC (Here in Spain)

6ºC (Cold water)

55 (lighter)

Thank you!

Quote 0 0
thiagoralves
Alright. Paste this on your HWLCB, connect your 1-Wire sensor to %IX0.2 (Raspberry Pi pin 7) and start the runtime. Your readings should show up at address %IW0. Since %IW0 is an integer, the value is the temperature (with decimals) multiplied by 100. Keep in mind that this HWLCB disables %IX0.2 from the Raspberry Pi driver since this pin is used to connect to the 1-Wire sensor. So even after you disconnect your sensor, you won't be able to use %IX0.2 on your Pi until you erase this HWLCB.
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
//-----------------------------------------------------------------------------
// DISCLAIMER: EDDITING THIS FILE CAN BREAK YOUR OPENPLC RUNTIME! IF YOU DON'T
// KNOW WHAT YOU'RE DOING, JUST DON'T DO IT. EDIT AT YOUR OWN RISK.
//
// PS: You can always restore original functionality if you broke something
// in here by clicking on the "Restore Original Code" button above.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// These are the ignored I/O vectors. If you want to override how OpenPLC
// handles a particular input or output, you must put them in the ignored
// vectors. For example, if you want to override %IX0.5, %IX0.6 and %IW3
// your vectors must be:
//     int ignored_bool_inputs[] = {5, 6}; //%IX0.5 and %IX0.6 ignored
//     int ignored_int_inputs[] = {3}; //%IW3 ignored
//
// Every I/O on the ignored vectors will be skipped by OpenPLC hardware layer
//-----------------------------------------------------------------------------
int ignored_bool_inputs[] = {2}; //sensor is connected to pin %IX0.2 so we need to disable it
int ignored_bool_outputs[] = {-1};
int ignored_int_inputs[] = {0}; //we are writing 1-wire sensor readings to %IW0
int ignored_int_outputs[] = {-1};
char devPath[128]; 
//-----------------------------------------------------------------------------
// This function is called by the main OpenPLC routine when it is initializing.
// Hardware initialization procedures for your custom layer should be here.
//-----------------------------------------------------------------------------
void initCustomLayer()
{
    unsigned char log_msg[1000];
    DIR *dir;
    struct dirent *dirent;
    char dev[16];
    char path[] = "/sys/bus/w1/devices";
    dir = opendir(path);
    if (dir != NULL)
    {
        while ((dirent = readdir(dir)))
            // 1-wire devices are links beginning with 28-
            if (dirent->d_type == DT_LNK && strstr(dirent->d_name, "28-") != NULL)
            {
                strcpy(dev, dirent->d_name);
                printf("\nDevice: %s\n", dev);
            }
        (void)closedir(dir);
    }
    else
    {
        sprintf(log_msg, "Couldn't open the w1 devices directory\n");
        log(log_msg);
    }
    // Assemble path to OneWire device
    sprintf(devPath, "%s/%s/w1_slave", path, dev);
}
//-----------------------------------------------------------------------------
// This function is called by OpenPLC in a loop. Here the internal input
// buffers must be updated with the values you want. Make sure to use the mutex 
// bufferLock to protect access to the buffers on a threaded environment.
//-----------------------------------------------------------------------------
void updateCustomIn()
{
    // Example Code - Overwritting %IW3 with a fixed value
    // If you want to have %IW3 constantly reading a fixed value (for example, 53)
    // you must add %IW3 to the ignored vectors above, and then just insert this 
    // single line of code in this function:
    //     if (int_input[3] != NULL) *int_input[3] = 53;
    unsigned char log_msg[1000];
    char tmpData[6];
    ssize_t numRead;
    char buf[256];
    int fd = open(devPath, O_RDONLY);
    if (fd == -1)
    {
        sprintf(log_msg, "Couldn't open the w1 device\n");
        log(log_msg);
    }
    while ((numRead = read(fd, buf, 256)) > 0)
    {
        strncpy(tmpData, strstr(buf, "t=") + 2, 5);
        float tempC = strtof(tmpData, NULL);
    
        //save value in OpenPLC buffer
        if (int_input[0] != NULL) *int_input[0] = (uint16_t)(tempC * 100);
    }
    close(fd);
}
//-----------------------------------------------------------------------------
// This function is called by OpenPLC in a loop. Here the internal output
// buffers must be updated with the values you want. Make sure to use the mutex 
// bufferLock to protect access to the buffers on a threaded environment.
//-----------------------------------------------------------------------------
void updateCustomOut()
{
    // Example Code - Sending %QW5 value over I2C
    // If you want to have %QW5 output to be sent over I2C instead of the
    // traditional output for your board, all you have to do is, first add
    // %QW5 to the ignored vectors, and then define a send_over_i2c()
    // function for your platform. Finally you can call send_over_i2c() to 
    // send your %QW5 value, like this:
    //     if (int_output[5] != NULL) send_over_i2c(*int_output[5]);
    //
    // Important observation: If your I2C pins are used by OpenPLC I/Os, you
    // must also add those I/Os to the ignored vectors, otherwise OpenPLC
    // will try to control your I2C pins and your I2C message won't work.
}
Quote 0 0
arraiz
Thanks and sorry for the delay, Im have been fighting with mi ISP for 2 weeks here.
I have successfully connected the sensor and the added the code to te HWLCB and compiled correctly but I think that the readings are wrong.

Screenshot 2020-04-20 at 17.34.34.png 
I modified the program a Little to get the sensor reading in the "Monitoring" of the OpenPLC Dashboard
There I added a New Variable called Sensor with type Int and unsigned_int (tested both of them) but the readings are strange

Screenshot 2020-04-20 at 17.33.35.png  The sensor values are not expected and after certain time the value freezes.

Theres something I forget? the pin is well conected at GPIO4 (PIN 7 of the PI)




Quote 0 0
thiagoralves
Use UINT instead of INT. Also, show me your Dashboard logs to make sure there isn't anything wrong with the readings
Quote 0 0
arraiz
Thanks again, the logs is this

OpenPLC Runtime starting...
Interactive Server: Listening on port 43628
Warning: Persistent Storage file not found
Issued start_modbus() command to start on port: 502
Server: Listening on port 502
Server: waiting for new client...
Issued start_dnp3() command to start on port: 20000
DNP3 ID manager: Starting thread (0)
DNP3 ID DNP3_Server: Listening on: 0.0.0.0:20000
Issued start_enip() command to start on port: 44818
Server: Listening on port 44818
Server: waiting for new client...
Issued stop_pstorage() command
Server: Client accepted! Creating thread for the new client ID: 16...
Server: waiting for new client...
Server: Thread created for client ID: 16
Modbus Server: Something is wrong with the client ID: 16 message Size : -1
Terminating Modbus connections thread


Also I have changed the varable type por UINT, but the reading is wrong ):

Screenshot 2020-04-20 at 18.59.17.png
Quote 0 0
thiagoralves
This is weird. I had copied and pasted exactly the same code you sent me over to HWLCB format. The only thing I did differently was to convert the reading to UINT which is the standard way OpenPLC reads analog sensors. I don't have a DS18B20 sensor so it is hard for me to debug here. Hopefully there is just a silly mistake somewhere on the UINT conversion. Try the code below please. What I did on this code was to output the readings the same way your sample code outputs them. They will be printed on the Dashboard logs. Let me know if the Dashboard readings are working.
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
//-----------------------------------------------------------------------------
// DISCLAIMER: EDDITING THIS FILE CAN BREAK YOUR OPENPLC RUNTIME! IF YOU DON'T
// KNOW WHAT YOU'RE DOING, JUST DON'T DO IT. EDIT AT YOUR OWN RISK.
//
// PS: You can always restore original functionality if you broke something
// in here by clicking on the "Restore Original Code" button above.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// These are the ignored I/O vectors. If you want to override how OpenPLC
// handles a particular input or output, you must put them in the ignored
// vectors. For example, if you want to override %IX0.5, %IX0.6 and %IW3
// your vectors must be:
//     int ignored_bool_inputs[] = {5, 6}; //%IX0.5 and %IX0.6 ignored
//     int ignored_int_inputs[] = {3}; //%IW3 ignored
//
// Every I/O on the ignored vectors will be skipped by OpenPLC hardware layer
//-----------------------------------------------------------------------------
int ignored_bool_inputs[] = {2}; //sensor is connected to pin %IX0.2 so we need to disable it
int ignored_bool_outputs[] = {-1};
int ignored_int_inputs[] = {0}; //we are writing 1-wire sensor readings to %IW0
int ignored_int_outputs[] = {-1};
char devPath[128]; 
//-----------------------------------------------------------------------------
// This function is called by the main OpenPLC routine when it is initializing.
// Hardware initialization procedures for your custom layer should be here.
//-----------------------------------------------------------------------------
void initCustomLayer()
{
    unsigned char log_msg[1000];
    DIR *dir;
    struct dirent *dirent;
    char dev[16];
    char path[] = "/sys/bus/w1/devices";
    dir = opendir(path);
    if (dir != NULL)
    {
        while ((dirent = readdir(dir)))
            // 1-wire devices are links beginning with 28-
            if (dirent->d_type == DT_LNK && strstr(dirent->d_name, "28-") != NULL)
            {
                strcpy(dev, dirent->d_name);
                printf("\nDevice: %s\n", dev);
            }
        (void)closedir(dir);
    }
    else
    {
        sprintf(log_msg, "Couldn't open the w1 devices directory\n");
        log(log_msg);
    }
    // Assemble path to OneWire device
    sprintf(devPath, "%s/%s/w1_slave", path, dev);
}
//-----------------------------------------------------------------------------
// This function is called by OpenPLC in a loop. Here the internal input
// buffers must be updated with the values you want. Make sure to use the mutex 
// bufferLock to protect access to the buffers on a threaded environment.
//-----------------------------------------------------------------------------
void updateCustomIn()
{
    // Example Code - Overwritting %IW3 with a fixed value
    // If you want to have %IW3 constantly reading a fixed value (for example, 53)
    // you must add %IW3 to the ignored vectors above, and then just insert this 
    // single line of code in this function:
    //     if (int_input[3] != NULL) *int_input[3] = 53;
    unsigned char log_msg[1000];
    char tmpData[6];
    ssize_t numRead;
    char buf[256];
    int fd = open(devPath, O_RDONLY);
    if (fd == -1)
    {
        sprintf(log_msg, "Couldn't open the w1 device\n");
        log(log_msg);
    }
    while ((numRead = read(fd, buf, 256)) > 0)
    {
        strncpy(tmpData, strstr(buf, "t=") + 2, 5);
        float tempC = strtof(tmpData, NULL);
    
        //save value in OpenPLC buffer
        if (int_input[0] != NULL) *int_input[0] = (uint16_t)(tempC * 100.0);
        
        //print values in Dashboard
        sprintf(log_msg, "Device: %s  - Temp: %.3f C  %.3f F\n\n", dev, (tempC / 1000), (tempC / 1000) * 9 / 5 + 32));
        log(log_msg);
    }
    close(fd);
}
//-----------------------------------------------------------------------------
// This function is called by OpenPLC in a loop. Here the internal output
// buffers must be updated with the values you want. Make sure to use the mutex 
// bufferLock to protect access to the buffers on a threaded environment.
//-----------------------------------------------------------------------------
void updateCustomOut()
{
    // Example Code - Sending %QW5 value over I2C
    // If you want to have %QW5 output to be sent over I2C instead of the
    // traditional output for your board, all you have to do is, first add
    // %QW5 to the ignored vectors, and then define a send_over_i2c()
    // function for your platform. Finally you can call send_over_i2c() to 
    // send your %QW5 value, like this:
    //     if (int_output[5] != NULL) send_over_i2c(*int_output[5]);
    //
    // Important observation: If your I2C pins are used by OpenPLC I/Os, you
    // must also add those I/Os to the ignored vectors, otherwise OpenPLC
    // will try to control your I2C pins and your I2C message won't work.
}
Quote 0 0
arraiz
There is something with the code now

Optimizing ST program...
Generating C files...
POUS.c
POUS.h
LOCATED_VARIABLES.h
VARIABLES.csv
Config0.c
Config0.h
Res0.c
Moving Files...
Compiling for Raspberry Pi
Generating object files...
Generating glueVars...
varName: __IX0_4  varType: BOOL
varName: __IW0  varType: UINT
varName: __QX0_0  varType: BOOL
Compiling main program...
In file included from hardware_layer.cpp:36:
custom_layer.h: In function ‘void updateCustomIn()’:
custom_layer.h:91:68: error: ‘dev’ was not declared in this scope
         sprintf(log_msg, "Device: %s  - Temp: %.3f C  %.3f F\n\n", dev, (tempC / 1000), (tempC / 1000) * 9 / 5 + 32));
                                                                    ^~~
custom_layer.h:91:68: note: suggested alternative: ‘div’
         sprintf(log_msg, "Device: %s  - Temp: %.3f C  %.3f F\n\n", dev, (tempC / 1000), (tempC / 1000) * 9 / 5 + 32));
                                                                    ^~~
                                                                    div
Error compiling C files
Compilation finished with errors!


Theres something with the dev variable now maybe this is a hint of what is happening?
Quote 0 0
thiagoralves
Ooops, my bad...
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
//-----------------------------------------------------------------------------
// DISCLAIMER: EDDITING THIS FILE CAN BREAK YOUR OPENPLC RUNTIME! IF YOU DON'T
// KNOW WHAT YOU'RE DOING, JUST DON'T DO IT. EDIT AT YOUR OWN RISK.
//
// PS: You can always restore original functionality if you broke something
// in here by clicking on the "Restore Original Code" button above.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// These are the ignored I/O vectors. If you want to override how OpenPLC
// handles a particular input or output, you must put them in the ignored
// vectors. For example, if you want to override %IX0.5, %IX0.6 and %IW3
// your vectors must be:
//     int ignored_bool_inputs[] = {5, 6}; //%IX0.5 and %IX0.6 ignored
//     int ignored_int_inputs[] = {3}; //%IW3 ignored
//
// Every I/O on the ignored vectors will be skipped by OpenPLC hardware layer
//-----------------------------------------------------------------------------
int ignored_bool_inputs[] = {2}; //sensor is connected to pin %IX0.2 so we need to disable it
int ignored_bool_outputs[] = {-1};
int ignored_int_inputs[] = {0}; //we are writing 1-wire sensor readings to %IW0
int ignored_int_outputs[] = {-1};
char devPath[128];
char dev[16];
//-----------------------------------------------------------------------------
// This function is called by the main OpenPLC routine when it is initializing.
// Hardware initialization procedures for your custom layer should be here.
//-----------------------------------------------------------------------------
void initCustomLayer()
{
    unsigned char log_msg[1000];
    DIR *dir;
    struct dirent *dirent;
    char path[] = "/sys/bus/w1/devices";
    dir = opendir(path);
    if (dir != NULL)
    {
        while ((dirent = readdir(dir)))
            // 1-wire devices are links beginning with 28-
            if (dirent->d_type == DT_LNK && strstr(dirent->d_name, "28-") != NULL)
            {
                strcpy(dev, dirent->d_name);
                sprintf(log_msg, "\nDevice: %s\n", dev);
                log(log_msg);
            }
        (void)closedir(dir);
    }
    else
    {
        sprintf(log_msg, "Couldn't open the w1 devices directory\n");
        log(log_msg);
    }
    // Assemble path to OneWire device
    sprintf(devPath, "%s/%s/w1_slave", path, dev);
}
//-----------------------------------------------------------------------------
// This function is called by OpenPLC in a loop. Here the internal input
// buffers must be updated with the values you want. Make sure to use the mutex 
// bufferLock to protect access to the buffers on a threaded environment.
//-----------------------------------------------------------------------------
void updateCustomIn()
{
    // Example Code - Overwritting %IW3 with a fixed value
    // If you want to have %IW3 constantly reading a fixed value (for example, 53)
    // you must add %IW3 to the ignored vectors above, and then just insert this 
    // single line of code in this function:
    //     if (int_input[3] != NULL) *int_input[3] = 53;
    unsigned char log_msg[1000];
    char tmpData[6];
    ssize_t numRead;
    char buf[256];
    int fd = open(devPath, O_RDONLY);
    if (fd == -1)
    {
        sprintf(log_msg, "Couldn't open the w1 device\n");
        log(log_msg);
    }
    while ((numRead = read(fd, buf, 256)) > 0)
    {
        strncpy(tmpData, strstr(buf, "t=") + 2, 5);
        float tempC = strtof(tmpData, NULL);
    
        //save value in OpenPLC buffer
        if (int_input[0] != NULL) *int_input[0] = (uint16_t)(tempC * 100.0);
        
        //print values in Dashboard
        sprintf(log_msg, "Device: %s  - Temp: %.3f C  %.3f F\n\n", dev, (tempC / 1000), (tempC / 1000) * 9 / 5 + 32));
        log(log_msg);
    }
    close(fd);
}
//-----------------------------------------------------------------------------
// This function is called by OpenPLC in a loop. Here the internal output
// buffers must be updated with the values you want. Make sure to use the mutex 
// bufferLock to protect access to the buffers on a threaded environment.
//-----------------------------------------------------------------------------
void updateCustomOut()
{
    // Example Code - Sending %QW5 value over I2C
    // If you want to have %QW5 output to be sent over I2C instead of the
    // traditional output for your board, all you have to do is, first add
    // %QW5 to the ignored vectors, and then define a send_over_i2c()
    // function for your platform. Finally you can call send_over_i2c() to 
    // send your %QW5 value, like this:
    //     if (int_output[5] != NULL) send_over_i2c(*int_output[5]);
    //
    // Important observation: If your I2C pins are used by OpenPLC I/Os, you
    // must also add those I/Os to the ignored vectors, otherwise OpenPLC
    // will try to control your I2C pins and your I2C message won't work.
}
Quote 0 0