GPIO Data Logger


64 posts   Page 2 of 3   1, 2, 3
by joan » Sun Oct 28, 2012 8:18 pm
To remove those characters do

tr -d "\240\302" <gpio-monitor.c >gpio-monitor2.c
User avatar
Posts: 4211
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK
by ScottBouch » Sun Oct 28, 2012 8:28 pm
Hi RGH,

I had been following the "learn Python the hard way" book, and pretty much re-typing code, you are right, it is a good way to learn! I've read through Joan's code, and tried to understand parts of it.

I may also drop Joan a PM with my email address, as time is getting tight, (winter is now upon us) and the boiler is still on the blink! I just need to get it fixed now...

Thanks, Scott.
http://www.scottbouch.posterous.com - - - - - - - - - Personal Blog
http://www.flightgear.posterous.com - - - - - - - - - - Aircrew Equipment blog
http://www.youtube.com/user/sbscottmonkey - - - YouTube Channel
User avatar
Posts: 146
Joined: Wed Jun 06, 2012 12:32 pm
Location: Midlands, UK
by ScottBouch » Sun Oct 28, 2012 9:01 pm
If it helps, here is the contents of my .c file:

Code: Select all
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <poll.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>

#define BCM2708_PERI_BASE 0x20000000
#define GPIO_BASE (BCM2708_PERI_BASE + 0x200000)

#define MIN_PIN 0
#define MAX_PIN 53

#define NUM_GPIO 54

#define PAGE_SIZE  4096
#define BLOCK_SIZE 4096

#define BANK1 13
#define BANK2 14

#define MILLION (1000000L)

volatile uint32_t *gpio ;

struct timeval now;

uint32_t monitorBank1, monitorBank2;
uint32_t displayBank1, displayBank2;
uint32_t inputBank1, inputBank2;
uint32_t legalBank1, legalBank2;

int trimChars;
int decimals;

int sampleRate, sampleInterval;
int usecs[]={1000000, 100000, 10000, 1000, 100, 10, 1};

char options[2048];

int initGpio(void)
{
   int      fd;
   uint8_t *gpioMem;
   struct timeval tv;

   if ((fd = open ("/dev/mem", O_RDWR | O_SYNC) ) < 0)
   {
     fprintf (stderr, "Unable to open /dev/mem: %s\n", strerror (errno)) ;
     exit(-1);
   }

   if ((gpioMem = malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL)
   {
     fprintf (stderr, "malloc failed: %s\n", strerror (errno)) ;
     exit(-1);
   }

   if (((uint32_t)gpioMem % PAGE_SIZE) != 0)
     gpioMem += PAGE_SIZE - ((uint32_t)gpioMem % PAGE_SIZE) ;

   gpio = (uint32_t *) mmap((caddr_t)gpioMem,
                            BLOCK_SIZE, PROT_READ|PROT_WRITE,
                            MAP_SHARED|MAP_FIXED, fd, GPIO_BASE) ;

   if ((int32_t)gpio < 0)
   {
     fprintf (stderr, "mmap failed: %s\n", strerror (errno)) ;
     exit(-1);
   }
}

void displayGPIOlevels(uint32_t bank1, uint32_t bank2)
{
   char buf[NUM_GPIO];
   int i, j;

   j = 0;

   for (i=0; i<32; i++)
   {
      if ((monitorBank1 & (1<<i)) || (displayBank1 & (1<<i)))
      {
         if (bank1 & (1<<i)) buf[j]='1'; else buf[j]='0';
         j++;
      }
   }

   for (i=0; i<22; i++)
   {
      if ((monitorBank2 & (1<<i)) || (displayBank2 & (1<<i)))
      {
         if (bank2 & (1<<i)) buf[j]='1'; else buf[j]='0';
         j++;
      }
   }

   buf[j]=0;

   printf("%s\n", buf);
}

void timestamp()
{
   /* statics persist over function calls */

   static struct timeval last;
   static char buf[32];

   struct tm tmp;

   if (now.tv_sec != last.tv_sec)
   {
      /* only reformat date/time once per second */

      last.tv_sec = now.tv_sec;
      localtime_r(&now.tv_sec, &tmp);
      strftime(buf, sizeof(buf), "%F@%T", &tmp);
   }

   if (decimals)
      printf("%s.%0*d ", buf+trimChars, decimals, now.tv_usec/usecs[decimals]);
   else
      printf("%s ", buf+trimChars);
}

typedef void (*pinFunc)(int);

void pinDisplay(int pin)
{
   if (pin < 32) displayBank1 |= (1<<pin);
   else          displayBank2 |= (1<<(pin-32));
}

void pinMonitor(int pin)
{
   if (pin < 32) monitorBank1 |= (1<<pin);
   else          monitorBank2 |= (1<<(pin-32));
}

void pinInput(int pin)
{
   int gpfsel, shift, legal;

   if (pin < 32) legal = legalBank1 & (1<<pin);
   else          legal = legalBank2 & (1<<(pin-32));

   if (legal)
   {
      if (pin < 32) inputBank1 |= (1<<pin);
      else          inputBank2 |= (1<<(pin-32));

      gpfsel = pin/10;
      shift = (pin%10)*3;
      *(gpio + gpfsel) &= ~(7 << shift);
   }
}

int parsePins(char * pins, pinFunc f)
{
   char * token;
   int i1, i2, t;

   token = strtok(pins, ",");

   while (token != NULL)
   {
      if (index(token, '-') != NULL)
      {
         t = sscanf(token, "%d-%d", &i1, &i2);
         if (t != 2) return -1;
      }
      else
      {
         i1 = atoi(token);
         i2 = i1;
      }

      if (i1 > i2) {t = i1; i1 = i2; i2 = t;}

      if ((i1<MIN_PIN) || (i2>MAX_PIN)) return -1;

      for (t=i1; t<=i2; t++) (f)(t);

      token = strtok(NULL, ",");
   }
   return 0;
}

void usage()
{
   fprintf(stderr, "\n" \
      "Usage: sudo ./gpio-monitor [OPTION] ...\n" \
      "   -d RANGE, gpios to display, default NONE\n" \
      "       (additional to those being monitored)\n" \
      "   -i RANGE, gpios to set as inputs, default NONE\n" \
      "       (0-5,7-11,14-15,17-18,21-25,27 only legal, others ignored)\n" \
      "   -m RANGE, gpios to monitor, default NONE\n" \
      "   -s value, sample rate per second, 1-1000000, default 100\n" \
      "   -t value, characters to trim from date/time, 0-18, default 0\n" \
      "RANGE may be pin or pin1-pin2 or any combination\n" \
      "      separated by commas without spaces\n" \
      "      (pin numbers range from 0 to 53)\n" \
      "      e.g. 0-31,36,40,50-53\n" \
      "EXAMPLE\n" \
      "sudo ./gpio-monitor -m0-7 -d11,15-18 -s200\n" \
      "  Monitor state changes on gpios 0 to 7 at 200 Hz.\n" \
      "  Also display the states of gpios 11 and 15 to 18\n" \
      "  when one of 0 to 7 changes state.\n" \
   "\n");
}

int initOpts(int argc, char *argv[])
{
   int opt, include, status;

   trimChars = 0;
   decimals = 2;
   sampleRate = 100;
   sampleInterval = MILLION / sampleRate;

   monitorBank1=0x00000000;
   monitorBank2=0x00000000;

   displayBank1=0x00000000;
   displayBank2=0x00000000;

   inputBank1=0x00000000;
   inputBank2=0x00000000;

   /*          31     24      16       8       0  */
   /*           |      |       |       |       |  */
   legalBank1=0b00001011111001101100111110111111;

   /*                    53   48      40       32 */
   /*                     |    |       |       |  */
   legalBank2=0b00000000000000000000000000000000;

   while ((opt = getopt(argc, argv, "d:i:m:s:t:")) != -1)
   {
      switch (opt)
      {
         case 'd':
            status = parsePins(optarg, pinDisplay);
            if (status < 0)
            {
               usage();
               exit(-1);
            }
            break;

         case 'i':
            status = parsePins(optarg, pinInput);
            if (status < 0)
            {
               usage();
               exit(-1);
            }
            break;

         case 'm':
            status = parsePins(optarg, pinMonitor);
            if (status < 0)
            {
               usage();
               exit(-1);
            }
            break;

        case 's':

            sampleRate = atoi(optarg);

            if ((sampleRate<1) || (sampleRate>MILLION))
            {
               usage();
               exit(-1);
            }

            if      (sampleRate <= 1) decimals = 0;
            else if (sampleRate <= 10) decimals = 1;
            else if (sampleRate <= 100) decimals = 2;
            else if (sampleRate <= 1000) decimals = 3;
            else if (sampleRate <= 10000) decimals = 4;
            else if (sampleRate <= 100000) decimals = 5;
            else                            decimals = 6;

            sampleInterval = MILLION / sampleRate;

            break;

         case 't':
            trimChars=atoi(optarg);
            if ((trimChars<0) || (trimChars>18))
            {
               usage();
               exit(-1);
            }
            break;

        default: /* '?' */
           usage();
           exit(-1);
        }
    }
}

void formatPins(char * str, uint32_t p1, uint32_t p2)
{
   int i;
   char buf[8];

   if (p1 || p2)
   {
      for (i=MIN_PIN; i<=MAX_PIN; i++)
      {
         if (i<32)
         {
            if (p1 & (1<<i)) {sprintf(buf, " %d", i); strcat(str, buf);}
         }
         else
         {
            if (p2 & (1<<(i-32))) {sprintf(buf, " %d", i); strcat(str, buf);}
         }
      }
   }
   else strcat(str, "NONE");
}

void formatOpts(char * str)
{
   int i;
   char buf[256];

   struct tm tmp;

   gettimeofday(&now, NULL);
   localtime_r(&now.tv_sec, &tmp);
   strftime(buf, sizeof(buf), "#0 %F@%T\n", &tmp);
   strcat(str, buf);

   sprintf(buf, "#1 Sample rate (Hz): %d\n", sampleRate);
   strcat(str, buf);

   sprintf(buf, "#2 Sample interval (microseconds): %d\n", sampleInterval);
   strcat(str, buf);

   sprintf(buf, "#3 Trim date/time characters: %d\n", trimChars);
   strcat(str, buf);

   sprintf(buf, "#4 Seconds decimal places: %d\n", decimals);
   strcat(str, buf);

   sprintf(buf, "#5 Monitoring gpios: ");
   formatPins(buf, monitorBank1, monitorBank2);
   strcat(str, buf);
   strcat(str, "\n");

   sprintf(buf, "#6 Also displaying gpios: ");
   formatPins(buf, displayBank1, displayBank2);
   strcat(str, buf);
   strcat(str, "\n");

   sprintf(buf, "#7 Setting gpios as inputs: ");
   formatPins(buf, inputBank1, inputBank2);
   strcat(str, buf);
   strcat(str, "\n");

   sprintf(buf, "#8 gpios display order: ");
   formatPins(buf, monitorBank1|displayBank1, monitorBank2|displayBank2);
   strcat(str, buf);
   strcat(str, "\n");

   strcat(str, "#9 \n");
   strcat(str, "#A \n");
   strcat(str, "#B \n");
   strcat(str, "#C \n");
   strcat(str, "#D \n");
   strcat(str, "#E \n");
   strcat(str, "#F \n");
}

int main(int argc, char *argv[])
{
   uint32_t oldBank1, oldBank2, newBank1, newBank2;
   int seconds, micros;
   int diff;
   struct timeval next;

   initGpio();

   initOpts(argc, argv);

   formatOpts(options);

   printf("%s", options);

   oldBank1 = *(gpio + BANK1);
   oldBank2 = *(gpio + BANK2);

   /* arbitrarily start sampling on a second boundary */

   gettimeofday(&now, NULL);

   next.tv_sec = now.tv_sec+1;
   next.tv_usec = 0;
 
   while(1)
   {
      gettimeofday(&now, NULL);

      seconds = now.tv_sec  - next.tv_sec;
      micros  = now.tv_usec - next.tv_usec;
      diff = (seconds * MILLION) + micros;

      if (diff >= 0)
      {
         newBank1 = *(gpio + BANK1);
         newBank2 = *(gpio + BANK2);

         if (((newBank1&monitorBank1) != (oldBank1&monitorBank1)) ||
             ((newBank2&monitorBank2) != (oldBank2&monitorBank2)))
         {
            timestamp();

            displayGPIOlevels(newBank1, newBank2);

            oldBank1 = newBank1;
            oldBank2 = newBank2;
         }

         next.tv_usec += sampleInterval;
         if (next.tv_usec >= MILLION)
         {
            next.tv_usec -= MILLION;
            next.tv_sec += 1;
         }
      }
      if (sampleRate < 1000) usleep(250);
   }
}


Joan, I used: pi@raspberrypi ~ $ tr -d "\240\302" <gpio-monitor.c and it displayed the code in my Terminal. Is there something I needed to do after this to save changes etc?

I then tried building it again with cc and got the same problem...

Cheers, Scott
http://www.scottbouch.posterous.com - - - - - - - - - Personal Blog
http://www.flightgear.posterous.com - - - - - - - - - - Aircrew Equipment blog
http://www.youtube.com/user/sbscottmonkey - - - YouTube Channel
User avatar
Posts: 146
Joined: Wed Jun 06, 2012 12:32 pm
Location: Midlands, UK
by joan » Sun Oct 28, 2012 9:03 pm
You need to add the >gpio-monitor2.c to the end of the line to send output to a new file.
User avatar
Posts: 4211
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK
by ScottBouch » Sun Oct 28, 2012 9:13 pm
Thanks Joan... sorry, my fault.

OOOOH! This is very exciting! tr worked, so I went ahead with cc it has built an executable!

Now it's playtime!!!! Thanks Joan!!!
http://www.scottbouch.posterous.com - - - - - - - - - Personal Blog
http://www.flightgear.posterous.com - - - - - - - - - - Aircrew Equipment blog
http://www.youtube.com/user/sbscottmonkey - - - YouTube Channel
User avatar
Posts: 146
Joined: Wed Jun 06, 2012 12:32 pm
Location: Midlands, UK
by ScottBouch » Sun Oct 28, 2012 9:56 pm
It WORKS!!!!

GPIO 00 and 01 changing state with some pushbuttons I've thrown together... monitoring GPIO 0 to 5...

Code: Select all
2012-10-28@21:51:56.98 011100
2012-10-28@21:51:58.76 111100
2012-10-28@21:52:02.65 101100
2012-10-28@21:52:03.59 111100
2012-10-28@21:52:07.25 101100
2012-10-28@21:52:07.45 111100


Big thank-you to Joan - Much beer owed!!!! I can't thank you enough!

So pleasing that it has worked, it was a real pleasure to see the states changing on the screen!!! :D

Thanks again, Scott.
http://www.scottbouch.posterous.com - - - - - - - - - Personal Blog
http://www.flightgear.posterous.com - - - - - - - - - - Aircrew Equipment blog
http://www.youtube.com/user/sbscottmonkey - - - YouTube Channel
User avatar
Posts: 146
Joined: Wed Jun 06, 2012 12:32 pm
Location: Midlands, UK
by joan » Sun Oct 28, 2012 10:01 pm
That's good. I hope it helps you track down the fault. :)
User avatar
Posts: 4211
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK
by ScottBouch » Sun Oct 28, 2012 10:04 pm
joan wrote:That's good. I hope it helps you track down the fault. :)


I'm sure it will.... Thank you again. I'll update this thread with the results!

Cheers, Scott.
http://www.scottbouch.posterous.com - - - - - - - - - Personal Blog
http://www.flightgear.posterous.com - - - - - - - - - - Aircrew Equipment blog
http://www.youtube.com/user/sbscottmonkey - - - YouTube Channel
User avatar
Posts: 146
Joined: Wed Jun 06, 2012 12:32 pm
Location: Midlands, UK
by ScottBouch » Sun Oct 28, 2012 10:10 pm
Quick question...

How do I terminate / exit the program to get back to my Terminal prompt? Does it time-out?

Cheers, Scott.
http://www.scottbouch.posterous.com - - - - - - - - - Personal Blog
http://www.flightgear.posterous.com - - - - - - - - - - Aircrew Equipment blog
http://www.youtube.com/user/sbscottmonkey - - - YouTube Channel
User avatar
Posts: 146
Joined: Wed Jun 06, 2012 12:32 pm
Location: Midlands, UK
by joan » Sun Oct 28, 2012 10:30 pm
There is no timeout.

Press control C in the window to kill the process.

If you make it a background process you can use kill to end the process.
joan@pluto:~$ sudo ./gpio-monitor -m0-15 >logfile &
[2] 2232
joan@pluto:~$ tail logfile
2012-10-28@22:24:55.94 11110011110000111000000000000000
2012-10-28@22:24:55.95 11110011100000111000000000000000
2012-10-28@22:24:55.96 11110011110000111000000000000000
2012-10-28@22:24:55.97 11110011100000111000000000000000
2012-10-28@22:24:55.98 11110011110000111000000000000000
2012-10-28@22:24:55.99 11110011100000111000000000000000
2012-10-28@22:24:56.00 11110011110000111000000000000000
2012-10-28@22:24:56.01 11110011100000111000000000000000
2012-10-28@22:24:56.02 11110011110000111000000000000000
joan@pluto:~$ tail logfile
2012-10-28@22:24:58.73 11110011100000111000000000000000
2012-10-28@22:24:58.74 11110011110000111000000000000000
2012-10-28@22:24:58.75 11110011100000111000000000000000
2012-10-28@22:24:58.76 11110011110000111000000000000000
2012-10-28@22:24:58.77 11110011100000111000000000000000
2012-10-28@22:24:58.78 11110011110000111000000000000000
2012-10-28@22:24:58.79 11110011100000111000000000000000
2012-10-28@22:24:58.80 11110011110000111000000000000000
2012-10-28@22:24:58.81 11110011100000111000000000000000
joan@pluto:~$ sudo kill 2232
[2]+ Exit 143 sudo ./gpio-monitor -m0-15 > logfile
joan@pluto:~$
User avatar
Posts: 4211
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK
by ScottBouch » Sun Oct 28, 2012 10:48 pm
Aaaaahh... now I understand what that 4 digit number is when I have used the >log method. it's a way of identifying the background tasks! (learning lots here!)

Speaking of which, i've not managed to get the log file to record anything yet... Printing to the screen works very well in real time (most impressed!) but my log files always end up 0bytes.

That's what made me wonder if there is a special way of exiting, to save changes to the file?

Cheers, Scott.
http://www.scottbouch.posterous.com - - - - - - - - - Personal Blog
http://www.flightgear.posterous.com - - - - - - - - - - Aircrew Equipment blog
http://www.youtube.com/user/sbscottmonkey - - - YouTube Channel
User avatar
Posts: 146
Joined: Wed Jun 06, 2012 12:32 pm
Location: Midlands, UK
by ScottBouch » Sun Oct 28, 2012 10:54 pm
My Pi had started to operate slowly... so using what you've just taught me, I usaed top to end some processes...... These were all instances of gpio-monitor that I'd started inadvertantly in trying to get the >log working!!

Code: Select all
pi@raspberrypi ~ $ sudo kill 2673
pi@raspberrypi ~ $ sudo kill 2674
pi@raspberrypi ~ $ sudo kill 2627
pi@raspberrypi ~ $ sudo kill 2552
pi@raspberrypi ~ $ sudo kill 2598
pi@raspberrypi ~ $ sudo kill 2574
pi@raspberrypi ~ $ sudo kill 2655
pi@raspberrypi ~ $ sudo kill 2635
http://www.scottbouch.posterous.com - - - - - - - - - Personal Blog
http://www.flightgear.posterous.com - - - - - - - - - - Aircrew Equipment blog
http://www.youtube.com/user/sbscottmonkey - - - YouTube Channel
User avatar
Posts: 146
Joined: Wed Jun 06, 2012 12:32 pm
Location: Midlands, UK
by joan » Sun Oct 28, 2012 11:06 pm
When the output is being sent to a file it is buffered in memory and only written out when the buffer is full. If the buffer isn't filled it is not written. I don't catch the control C and write the buffer out.

To ensure the buffer is always written make the following change to the source code and recompile.

Go to line 107 which should read

printf("%s\n", buf);

After that line add the following line

fflush(NULL);

The fflush forces the data to be written to disc immediately. That would impact performance if you were logging a great deal of data but that is probably not going to be the case for your usage.
User avatar
Posts: 4211
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK
by ScottBouch » Mon Oct 29, 2012 12:48 pm
joan wrote:When the output is being sent to a file it is buffered in memory and only written out when the buffer is full. If the buffer isn't filled it is not written. I don't catch the control C and write the buffer out.

To ensure the buffer is always written make the following change to the source code and recompile.

Go to line 107 which should read

printf("%s\n", buf);

After that line add the following line

fflush(NULL);

The fflush forces the data to be written to disc immediately. That would impact performance if you were logging a great deal of data but that is probably not going to be the case for your usage.


Thank you... I ran out of time last night, but will have a go tonight!

I guess the question of how big the buffer size is depends on how many I/O I'm logging, but do you have a rough idea based on lets say 8 inputs, how many lines it will take to fill the buffer and write to the log file?

Thanks, Scott.
http://www.scottbouch.posterous.com - - - - - - - - - Personal Blog
http://www.flightgear.posterous.com - - - - - - - - - - Aircrew Equipment blog
http://www.youtube.com/user/sbscottmonkey - - - YouTube Channel
User avatar
Posts: 146
Joined: Wed Jun 06, 2012 12:32 pm
Location: Midlands, UK
by ScottBouch » Thu Nov 01, 2012 10:10 am
Now all the parts have arrived, I've started putting together my interface board!

rps20121101_100441.jpg
Workbench
rps20121101_100441.jpg (52.27 KiB) Viewed 1174 times


rps20121101_100528.jpg
Components
rps20121101_100528.jpg (43.2 KiB) Viewed 1174 times
http://www.scottbouch.posterous.com - - - - - - - - - Personal Blog
http://www.flightgear.posterous.com - - - - - - - - - - Aircrew Equipment blog
http://www.youtube.com/user/sbscottmonkey - - - YouTube Channel
User avatar
Posts: 146
Joined: Wed Jun 06, 2012 12:32 pm
Location: Midlands, UK
by ScottBouch » Sun Nov 04, 2012 8:32 am
Making and testing hardware with Joan's software, short videos:

Part 1: http://youtu.be/zI-46zi-kWE

Part 2: http://youtu.be/8MPLQEQiMEA

Cheers, Scott
http://www.scottbouch.posterous.com - - - - - - - - - Personal Blog
http://www.flightgear.posterous.com - - - - - - - - - - Aircrew Equipment blog
http://www.youtube.com/user/sbscottmonkey - - - YouTube Channel
User avatar
Posts: 146
Joined: Wed Jun 06, 2012 12:32 pm
Location: Midlands, UK
by joan » Sun Nov 04, 2012 9:04 am
Very informative videos.

My one and only experiment with mains power was after learning about electrolysis at school.

Glass full of water. Put mains lead from light swich into water. Switch on. Result. Loud bang, glass shattered, fuse blown. Don't try that at home!
User avatar
Posts: 4211
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK
by ScottBouch » Sun Nov 04, 2012 11:00 am
Haha... Bet that took you by surprise!

I learnt respect for msins at 4 years old when I purposefully touched 240V to see what it felt like...

I knew it wasn't going to be fun, but I needed to know for myself! Since then I've treated it with great care...

Cheers, Scott
http://www.scottbouch.posterous.com - - - - - - - - - Personal Blog
http://www.flightgear.posterous.com - - - - - - - - - - Aircrew Equipment blog
http://www.youtube.com/user/sbscottmonkey - - - YouTube Channel
User avatar
Posts: 146
Joined: Wed Jun 06, 2012 12:32 pm
Location: Midlands, UK
by ScottBouch » Sun Nov 04, 2012 8:11 pm
I've added a couple more videos:

Part 3:
http://youtu.be/E3ajZ3zJmto

Part 4:
http://youtu.be/t-n8pnv9YPw

Using my oscilloscope to find out why i was getting random counts, then selecting thr right capacitor value. Had to do a quick trip to Maplin for some more capacitors! And now Maplins only stock 2 of any value component! How stupid, so i bought a selection pack...

Cheers, Scott
http://www.scottbouch.posterous.com - - - - - - - - - Personal Blog
http://www.flightgear.posterous.com - - - - - - - - - - Aircrew Equipment blog
http://www.youtube.com/user/sbscottmonkey - - - YouTube Channel
User avatar
Posts: 146
Joined: Wed Jun 06, 2012 12:32 pm
Location: Midlands, UK
by mmaes » Wed Nov 07, 2012 11:18 pm
Hello Scott and Joan,
Seems to me that you are describing something I could be interested in.
I'm interested in reading the S0 pulses from the energy meters in my fusebox. When we remodelled our home in 2006 I had a 12-group fusebox installed, instead of the old 3-group. Later I installed energy meters (DMM Metering ADM1D V1.1) on 8 of the 12 groups, to be able to track energy consumption. Sadly the LCD's on the meters died. However, the meters have got S0's. So now I am thinking about buying a Raspberry and build me something that could read out the meters. However, I could use quite some help with this - and I hope I've found a starting point.
I do have a technical and IT background (and some Linux knowledge), and I've installed all electric wiring myself during the remodelling. But somehow I tend to get blue smoke whenever I get into electronics... :?
So the help I'm looking for is the following:
- the circuit design for the interface board between the RaspPi and the meters;
- a safe way to provide the energy meters with the requested power;
- and probably some help on the code...

What my basic plan is:
- provide 8 (possibly 12) meters with the requested power for S0-pulses;
- this circuit should be decoupled from the Raspberry's internal circuit (and knowing myself it should provide for the needed safeties... :oops:) ;
- have the Raspberry count and timestamp the pulses of 8 (posssibly 12) meters;
- send the data to a database on one of my NAS'es (probably a MySQL on the Thecus);
- make a web-frontend on the NAS that shows power consumption on the various groups.
That's all... 8-)

To give you some extra info, here is the literal information on the S0-pulses from the ADM manual (yes, it's Chinese):
The ADM1D DIN rail energy meter is equipped with a pulse output which is fully separated from the inside circuit. That generates pulses in proportion to the measured energy for remote reading purposes and accuracy testing. The pulse output is a polarity dependant, passive transistor output requiring an external voltage source for correct operation. For this external voltage source, the voltage (Ui) should be 5-27V DC, and the maximum input current (Iimax) is 27mA DC. To connect the impulse output, connect 5-27V DC to connector 20 (anode), and the signal wire (S) to connector 21 (cathode). The meter pulses 2000 per kWh (0.5Wh/imp).


I hope you can help me get started with this, for I think that the CV-project you've done comes pretty close to what I'm trying to achieve.

Thanks and regards in advance,
Marc
Posts: 15
Joined: Wed Nov 07, 2012 8:28 pm
by ScottBouch » Thu Nov 08, 2012 1:39 am
Hey MMAES,

I can help on the electronics side... From what you shared from the manual, it seems pretty straightforward.

What output are you looking for? Somethimg that logs, or something that displays an instantaneous reading?

My interface circuit is very nearly finished, will share some photos tomorrow.

Cheers, Scott
http://www.scottbouch.posterous.com - - - - - - - - - Personal Blog
http://www.flightgear.posterous.com - - - - - - - - - - Aircrew Equipment blog
http://www.youtube.com/user/sbscottmonkey - - - YouTube Channel
User avatar
Posts: 146
Joined: Wed Jun 06, 2012 12:32 pm
Location: Midlands, UK
by rgh » Thu Nov 08, 2012 8:23 am
"2000 per kWh".. I wonder how wide the pulses are and if a simple software polling approach will be sure to see them all. OTOH, if the rate is low and you want to do pretty graphs in real time you might be better off with generating interrupts from the pulse edges instead.
Posts: 219
Joined: Fri Nov 25, 2011 3:53 pm
by mmaes » Thu Nov 08, 2012 8:54 am
Hello Scott, thanks for your quick reaction.
My intention is primarily logging. Thus it is more important that I catch all pulses, than getting the exact timing right to the millisecond. Would it make a difference though?
Seems to me that if I'm able to to register the pulses, I can also show them in a near real-time graph.

@rgh: I don't own an oscilloscope, so I don't know how wide the actual pulses are. But since we run a 'normal' household :? the number of pulses shouldn't go off the scales...
The idea of using edge-triggered interrupts sounds appealing. Can the Raspberry generate an interrupt for every single GPIO - since I would be using 12?

Regards,
Marc
Posts: 15
Joined: Wed Nov 07, 2012 8:28 pm
by joan » Thu Nov 08, 2012 10:48 am
I think there is one interrupt line per bank of 32 gpios. All user accesible ones are currently in the same bank.

The interrupt would be raised for whichever gpio first triggered. ALL events would be recorded in the GPIO Pin Event Detect Status 0 register. I assume you woudn't get another interrupt until you'd acknowledged.

The event detect status registers are used to record level and edge events on the
GPIO pins. The relevant bit in the event detect status registers is set whenever: 1)
an edge is detected that matches the type of edge programmed in the rising/falling
edge detect enable registers, or 2) a level is detected that matches the type of level
programmed in the high/low level detect enable registers. The bit is cleared by
writing a “1” to the relevant bit.
The interrupt controller can be programmed to interrupt the processor when any of
the status bits are set. The GPIO peripheral has three dedicated interrupt lines.
Each GPIO bank can generate an independent interrupt. The third line generates a
single interrupt whenever any bit is set.


I had a quick play at using interrupts but was not successful (unless you think locking the machine up is a success :D ).

I probably did something wrong.

If your pulses are milliseconds long polling will be fine. If they are a few tens of microseconds long polling might work but you'd be using 90% of the cpu for that task.
User avatar
Posts: 4211
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK
by rgh » Thu Nov 08, 2012 8:42 pm
mmaes wrote:I don't own an oscilloscope, so I don't know how wide the actual pulses are. But since we run a 'normal' household :? the number of pulses shouldn't go off the scales...

It's the width of the pulse that is important, not the number, if you are just sampling in s/w. You say you don't have an oscilloscope, but you have a RaspberryPi... just search for Panalyzer on these forums. That'll sample at 1us resolution for a couple of seconds which should be fine to show you what the pulses look like. Whatever interface circuit you come up with for your logging will obviously be needed to hook up Panalyzer too.
Posts: 219
Joined: Fri Nov 25, 2011 3:53 pm