Solved Polling a sysctl value

Your work did indeed enter "55" correctly in my rrd database. Thanks So Much.
Code:
#include <stdio.h>
#iinclude <unistd.h>
#include <sys/sysctl.h>
#include <rrd.h>

char  *updateparams[] = {"rrdupdate", "ow_temp.rrd", "N:55", NULL};

int main(int argc, const char *argv[])
{

   rrd_clear_error();
   int rc = rrd_update(3, updateparams);
   if (rc != 0)
   {
      printf("RRD Update failed with result code %d\n", rc);
      return rc;
   }

   return 0;
}

Now that worked without error. I took and stripped it down(Sorry I need to look at it in small chunks first).
The reason I don't need timestamp with rrdupdate is this I am trying to pass this value to rrd >>N:55<<
I changed timetemp to this. It signifies N(TIME NOW) and 55 is a database value(fake temp for testing).
In the future I need help making "55" a variable like ${tval} from the previous work plugged into the database.
So I need to pass it like this I guess??
char *updateparams[] = {"rrdupdate", "ow_temp.rrd", "N:${tval}", NULL}
 
OK could you please review this work and see if it is acceptable. It compiled without error or warnings.
I substituted the N:65 for N:${tval}. Is that OK??..

Code:
#include <stdio.h>
#include <unistd.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <rrd.h>

char  *updateparams[] = {"rrdupdate", "ow_temp.rrd", "N:${tval}", NULL};
int main(int argc, const char *argv[])
{
        size_t size=sizeof(int);
        int buf;
        double tval;
        sysctlbyname("dev.ow_temp.0.temperature", &buf, &size, NULL, 0);
        tval = (buf - 273150)*1.8e-3 + 32.0;

        rrd_clear_error();
        int rc = rrd_update(3, updateparams);
        if (rc != 0)
   {
      printf("RRD Update failed with result code %d\n", rc);
      return rc;
   }

   return 0;
}

"/c/rrd.c" 25 lines, 586 characters
 
I also have plans for more than one temp sensor.
Code:
int main()
{
       size_t size=sizeof(int);
       int buf0; int buf1; int buf2;
       double tval0; double tval1; double tval2;
       sysctlbyname("dev.ow_temp.0.temperature", &buf0, &size, NULL, 0);
       sysctlbyname("dev.ow_temp.1.temperature", &buf1, &size, NULL, 0);
       sysctlbyname("dev.ow_temp.2.temperature", &buf2, &size, NULL, 0);
       tval0 = (buf0 - 273150)*1.8e-3 + 32.0;
       tval1 = (buf1 - 273150)*1.8e-3 + 32.0;
       tval2 = (buf2 - 273150)*1.8e-3 + 32.0;
}

So I really need to add them to this line like?? N:${tval0}:{tval1}:{tval2}
char *updateparams[] = {"rrdupdate", "ow_temp.rrd", "N:${tval0}:{tval1}:{tval2}", NULL};
On the command line it would be like this rrdupdate ow_temp.rrd N:1:2:3. With the 3 data values each having a colon separator.
N being (now) and 1 being a stored value in the first db field, 2 being a value in the second field and 3 being the third field stored number.
rrd calls each 'db field' an DS or dataset.
For me a end value would look like N:72:73:74 with each temp being dumped into one rrd_update command from the variables tval0,tval1.tval2
 
OK could you please review this work and see if it is acceptable. It compiled without error or warnings.
I substituted the N:65 for N:${tval}. Is that OK??..

No, nOK.

You need a means to place the result of the sysctlbyname() call which was turned into tval into updateparams[].
Code:
#include <stdio.h>
#include <unistd.h>
#include <sys/sysctl.h>

#include <rrd.h>

size_t size = sizeof(int);
int    buf;
double tval;

char   temperature[32];
char  *updateparams[] = {"rrdupdate",  "ow_temp.rrd", temperature, NULL};

int main(int argc, const char *argv[])
{
   sysctlbyname("dev.cpu.0.temperature", &buf, &size, NULL, 0);
   tval = (buf - 273150)*1.8e-3 + 32.0;
   snprintf(temperature, 32, "N:%.4f", tval);

   optind = opterr = 0;
   rrd_clear_error();
   int rc = rrd_update(3, updateparams);
   if (rc != 0)
   {
      printf("RRD Update failed with result code %d -- %s\n", rc, timetemp);
      return rc;
   }
  
   return 0;
}
 
For more than one temperature, you would first increase the buffer size:
e.g. temperature[32] to temperatures[256]

Then you would add all the values by the way of the snprintf() call to the temperatures argument of
updateparams.

snprintf(temperatures, 256, "N:%.4f:%.4f:%.4f", tval0, tval1, tval2);
 
That all worked except small fix from 'timetemp' to 'temperature' in the error loop.
I now have live data graphing. One minute intervals currently via crontab.
temp_graph3.png


Thank you so much for the help.
Next I will add more inputs as per your directive. Might change names to temp0, temp1, ect.. to reflect the onewire names.
 
Well that was easy with your excellent instructions. Thanks So Much. I have 2 live sensors graphing now. Playing with an icecube on temp0.
Polling every 5 minutes with cron. Next up getting your daemon incorporated.

temp_graph3.png

Here is my graph command:
rrdtool graph temp_graph3.png -w 600 -h 150 -a PNG --vertical-label "Temperature ( F )" -s -2400 -e now DEF:temp0=ow_temp.rrd:temp0:MAX LINE2:temp0#00FF00:temp0 DEF:temp1=ow_temp.rrd:temp1:MAX LINE2:temp1#0000FF:temp1
 
I have worked my way up to six onewire sysctl inputs. That should be the most I need. Still working on getting all 6 graphed.
Originally i was doing 4 inputs..
Playing wth Hex Codes for each color on the graph. Smoothing needed next..

temp_graph3.png
 
Well I have a problem I can't seem to solve. I decided to also build a cpu_mon graphing app using coretemp and sysctl.
I am using this previous help from tobik@ to check the raw value sysctl is sending. It does not appear to be Kelvin or milliKelvin.
The value is coming out all wrong. I assume this is another value type. It appears the temp from sysctl dev.cpu.o.temperature is correct.
Code:
int                       
main()
{
        size_t size;
        int t_mK, t_C, t_F;
        size = sizeof t_mK;

        sysctlbyname("dev.cpu.0.temperature", &t_mK, &size, NULL, 0);

        t_C = (t_mK - 273150) / 1000;;
        t_F = (t_mK * 1.8 / 1000) - 459.67;
        printf("%d mK, %d C, %d F\n", t_mK, t_C, t_F);

        return 0;
}

"show_temp.c" 20 lines, 375 characters
root@gigabyte:/cpu_mon # cc show_temp.c -o show_temp.app
root@gigabyte:/cpu_mon # ./show_temp.app
3521 mK, -269 C, -453 F
root@gigabyte:/cpu_mon # sysctl dev.cpu.0.temperature
dev.cpu.0.temperature: 79.0C
I looked through sysctl.c with no luck.

Onewire temp reporting is working fine for me....
 
The core temperature seems to be reported in centiKelvin, try the following:

t_F = (t_cK - 2731.5)*0.18 + 32.0

Example: t_C = (3521 - 2731.5)*0.1 = 78.95 °C
Example: t_F = (3521 - 2731.5)*0.18 + 32 = 174.11 °F
 
I just found the relevant bits in coretemp/coretemp.c
Code:
ctx = device_get_sysctl_ctx(dev);

   oid = SYSCTL_ADD_NODE(ctx,
       SYSCTL_CHILDREN(device_get_sysctl_tree(pdev)), OID_AUTO,
       "coretemp", CTLFLAG_RD, NULL, "Per-CPU thermal information");

   /*
    * Add the MIBs to dev.cpu.N and dev.cpu.N.coretemp.
    */
   SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(pdev)),
       OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
       dev, CORETEMP_TEMP, coretemp_get_val_sysctl, "IK",
       "Current temperature");
Still not sure what part of that is applicable. I know the IK is what I need to convert. Thanks so much for saving me time.
https://reviews.freebsd.org/D3181
So looking here they call it deciKelvin.
 
Yes, of course it's deciKelvin (scale divided by 10, centiKelvin => 100, and milliKelvin => 1000). Sorry, my fault, I responded without having my first coffee.
 
My RPi2 was sleeping when no console connected. I figured that out (disabled powerd) and it's been keeping temps for almost 5 days now.
Really impressed. Now it's on to that 'C' daemon.
temp_graph2.png
 
I was having trouble with the daemon code. It compiles OK but throws up a warning.
I am using your earlier post as a test. https://forums.freebsd.org/posts/368330/
Code:
root@rpi2B:/rrd_tempd # cc rrd_tempd.c  -Wno-empty-body -Ofast -g0 -o rrd_tempd.app
rrd_tempd.c:162:48: warning: comparison of constant -1 with expression of type
      'char' is always true [-Wtautological-constant-out-of-range-compare]
   while ((ch = getopt(argc, argv, "p:fnt:h")) != -1)
          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^  ~~
1 warning generated.

So if i try to run the app it acts like it is not accepting the "-t" value and it repeats the default screen
Code:
root@rpi2B:/rrd_tempd # ./rrd_tempd.app -t 2

usage: rrd_tempd.app [-p file] [-f] [-n] [-t] [-h]
 -p file    the path to the pid file [default: /var/run/rrd_tempd.pid]
 -f         foreground mode, don't fork off as a daemon.
 -n         no console, don't fork off as a daemon - started/managed by launchd.
 -t         idle time in seconds, [default: 4 s].
 -h         shows these usage instructions.

root@rpi2B:/rrd_tempd #
This is the same as earlier with same results.
I see no processes in top or ps so I assume it is not running. Also the data file is not generated. Also I see no pid file in /var/run/
Thanks So Much
 
Got the RPi2 going with cron temp monitoring. It only has cpu.0.temperature for output.
temp_graph1.png


This is showing 3 ssh sessions each running a number crunching command.
dd if=/dev/urandom | bzip2 -9 >> /dev/null
 
I was having trouble with the daemon code. It compiles OK but throws up a warning.
I am using your earlier post as a test. https://forums.freebsd.org/posts/368330/
Code:
root@rpi2B:/rrd_tempd # cc rrd_tempd.c  -Wno-empty-body -Ofast -g0 -o rrd_tempd.app
rrd_tempd.c:162:48: warning: comparison of constant -1 with expression of type
      'char' is always true [-Wtautological-constant-out-of-range-compare]
   while ((ch = getopt(argc, argv, "p:fnt:h")) != -1)
          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^  ~~
1 warning generated.

I stumbled once again into the unsigned char trap. The default ABI of ARM expects char to be unsigned while all other ABI's expect signed chars. We could compile the code using clang rrd_temp.c -Wno-empty-body -Ofast -g0 -fsigned-char -o rrd_temp which would force the chars to be signed, however, a better option is to make the code ABI agnostic, so please, change line 157 -- the line right after the main() entry -- to:
Code:
   int         ch;
   char       *p;
My Beaglebone Black does not report a core temperature, so the daemon is running here, but it logs always 0 Kelvin = -459.67 °F
 
That worked wonders... Now I can get some smooth graphs..
I truly owe you.
I will try and send another 50 bucks this year to the foundation.
Any goodies you want just let me know..
Code:
root@rpi2B:/rrd_tempd # cat rrd_tempd.log | tail
2017-12-17 05:59:59   96.17 ^0F
2017-12-17 06:00:04   98.15 ^0F
2017-12-17 06:00:08   98.15 ^0F
2017-12-17 06:00:12   97.25 ^0F
2017-12-17 06:00:16   97.25 ^0F
2017-12-17 06:00:20   98.15 ^0F
2017-12-17 06:00:24   98.15 ^0F
2017-12-17 06:00:28   97.25 ^0F
2017-12-17 06:00:32   97.25 ^0F
2017-12-17 06:00:36   97.25 ^0F
So the next phase is output. Also what the heck am I really building!!
More like a valuable lesson in programming with pratical applications.
I have some 4-5 'Programming in C' books and I found all of them very dry.
The sample programs seemed nothing like what I wanted to build.
Now I have a slight recognition of argc and argv.
Aurument command and Argument variable ??. My guess.

Many of the items you showed were not in the books including snprintf.
Most manpages only show it as a subset of printf without much detail.

Next I want to do a command line 'C' app that displays a few live png graphs then maybe a web frontend with rrdcgi API.
 
argv[] is an array (vector) of pointers to the command line arguments passed to the program as plain nul-byte terminated strings. argc is the argument count, and it is at least 1, since the first argument in argv[0] contains always the full name of the program as it was given on the command line.

Regarding the C (and other language) books, I only use(d) them to pick the syntax and the language concepts and as a reference of course, for example for looking up what's the operator precedence today. Think of a carpenter, knowing what's all the various tools are for. These books serve well for this part, but are way less useful once you come to the creative work.

Say, you want your program process some input and output the results by some means - I suggest to start looking into a good reference documentation for the Standard C Library -- I quite like The GNU C Library. See:
You need to do some time/date processing - voilà:
You need to do some math processing:
And the syslog facility:
And so on ...

Basically, you want/need to do something and you start looking into the C Library, whether it got already facilities for getting the work done. If not, then you do a Google search on what you want to do, expressing it by a plain English full phrase. The Google search engine is equipped with a semantic analyzer and usually it comes up with more useful results if you explain what you want to do instead of searching for keywords.

Finally a good craftsman builds up his style, some people call this design patterns if it comes to programming.

As always, you only really learn something by actually doing it.
 
Last edited:
Regarding the books about C, my personal impression is that there is only one good book.
When I looked with disbelief at what you wrote, I went into my book room and grabbed my old, heavily worn out "The C Programming Language", 2nd edition, 1988 from K&R themselves.
I looked at appendix B1.2 "Formatted Output" and again was amazed how much information they managed to put together in a well-understandable manner onto only two pages.

If you don't have this book, get it.
I am sure, you will throw away the other 4-5 soon after you got that one from K&R.

And independent of the programming language, one should carefully watch the patterns one lets become habits, as obsigna indicates.
There are many books and text about this also.
 
Would you please check this code and the for() loop. I was really guessing here. It seems to work OK.
Code:
SNIP
      }
   }

   daemonize(dKind);

size_t size = sizeof(int);
int buf0,buf1,buf2,buf3;
double temp0,temp1,temp2,temp3;
char   temperature[256];
char  *updateparams[] = {"rrdupdate",  "/ow_rrd/ow_rrd.rrd", temperature, NULL};

for (;;)
{
                sleep(idle);
                sysctlbyname("dev.ow_temp.0.temperature", &buf0, &size, NULL, 0);
                temp0 = (buf0 - 273150)*1.8e-3 + 32.0;
                sysctlbyname("dev.ow_temp.1.temperature", &buf1, &size, NULL, 0);
                temp1 = (buf1 - 273150)*1.8e-3 + 32.0;
                sysctlbyname("dev.ow_temp.2.temperature", &buf2, &size, NULL, 0);
                temp2 = (buf2 - 273150)*1.8e-3 + 32.0;
                sysctlbyname("dev.ow_temp.3.temperature", &buf3, &size, NULL, 0);
                temp3 = (buf3 - 273150)*1.8e-3 + 32.0;

                snprintf(temperature, 256, "N:%.4f:%.4f:%.4f:%.4f", temp0,temp1,temp2,temp3);

                rrd_clear_error();
                int rc = rrd_update(3, updateparams);
                if (rc != 0)
                {
                printf("RRD Update failed with result code %d -- %s\n", rc, temperature);
                return rc;
                }
        }
}
 
I know the graphs look much smoother. Here again I am using an icecube to vary the temps. Using a -t 10 second value.
I am using a mounted usb stick for the database, figured that might help with the frequent write cycles.

temp_graph8.png
 
Last edited:
Would you please check this code and the for() loop. I was really guessing here. It seems to work OK.
Code:
SNIP
      }
   }

   daemonize(dKind);

size_t size = sizeof(int);
int buf0,buf1,buf2,buf3;
double temp0,temp1,temp2,temp3;
char   temperature[256];
char  *updateparams[] = {"rrdupdate",  "/ow_rrd/ow_rrd.rrd", temperature, NULL};

for (;;)
{
                sleep(idle);
                sysctlbyname("dev.ow_temp.0.temperature", &buf0, &size, NULL, 0);
                temp0 = (buf0 - 273150)*1.8e-3 + 32.0;
                sysctlbyname("dev.ow_temp.1.temperature", &buf1, &size, NULL, 0);
                temp1 = (buf1 - 273150)*1.8e-3 + 32.0;
                sysctlbyname("dev.ow_temp.2.temperature", &buf2, &size, NULL, 0);
                temp2 = (buf2 - 273150)*1.8e-3 + 32.0;
                sysctlbyname("dev.ow_temp.3.temperature", &buf3, &size, NULL, 0);
                temp3 = (buf3 - 273150)*1.8e-3 + 32.0;

                snprintf(temperature, 256, "N:%.4f:%.4f:%.4f:%.4f", temp0,temp1,temp2,temp3);

                rrd_clear_error();
                int rc = rrd_update(3, updateparams);
                if (rc != 0)
                {
                printf("RRD Update failed with result code %d -- %s\n", rc, temperature);
                return rc;
                }
        }
}

This looks quite good. Since the buf variables are used only for intermediate storage of the raw measurement data you could optimize your code a little bit by sharing one variable for all sensors - perhaps I would call it raw instead of buf, to make the purpose more clear. However this is only the dot of the i, and not very important.

The original code from which I took my example (the link which you gave in your post #14, http://permalink.gmane.org/gmane.comp.db.rrdtool.devel/894) resets the global variables optind and opterr of getopt(3) to zero before any call to rrd_clear_error() and rrd_update(). I cannot say whether this is really important, but it won't harm to do so, until you know exactly that resetting optind and opterr can be omited.

Anyway, my congratulations for your first working measurement daemon.
 
Back
Top