User avatar
ScottBouch
Posts: 146
Joined: Wed Jun 06, 2012 12:32 pm
Location: Midlands, UK
Contact: Website

GPIO Data Logger

Sat Oct 06, 2012 9:09 am

Has anyone done this yet?

I'm looking to log the states of the GPIO as 8 hardwired digital inputs.

Ideally logging on 'change of state' rather that at time intervals to keep files small, and it's only digitals.

I'd like my output to be a .csv file or .txt as a list of times and states.

This is in order to help me find an intermittent fault with my central heating boiler.

Cheers, Scott.
http://www.scottbouch.com

http://www.youtube.com/user/sbscottmonkey - - - YouTube Channel

User avatar
joan
Posts: 13621
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: GPIO Data Logger

Sat Oct 06, 2012 9:23 am

What sort of transitions do you want to catch? i.e. what is the shortest state change you want to be able to capture, microsecond, millisecond, second, tens of seconds?

User avatar
ScottBouch
Posts: 146
Joined: Wed Jun 06, 2012 12:32 pm
Location: Midlands, UK
Contact: Website

Re: GPIO Data Logger

Sat Oct 06, 2012 9:36 am

Hi Joan,

I should have put more specs forward...

What is the fastest rate I can get out if the Pi? I don't know how fast it needs to be due to the nature of the fault.. My intermittent fault may hang around for seconds, or microseconds.

As long as it captures the change of state, the timestamp accuracy doesn't really matter that much, i'm more interested in finding out which of my boilers temp / pressure switches is acting strangely. The length of tine that the fault is present for doesn't matter that much.

But 1ms accuracy would be nice for other applications though!

Cheers, Scott.
http://www.scottbouch.com

http://www.youtube.com/user/sbscottmonkey - - - YouTube Channel

User avatar
joan
Posts: 13621
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: GPIO Data Logger

Sat Oct 06, 2012 9:53 am

I have a somewhat similar program which works on microsecond timing to capture signals from infra-red remotes. A bit pointless as all the transitions I've seen are in 10s of microseconds.

My code is only interested in one gpio though and produces output such as
820843
-269
5083
-118
1640
-268
1514
-244
3270
-184
6182
-126
1483
-323
2356
-311
89955
-300
8559
-312
Positive high (1), negative low (0) in microseconds.

Code: Select all

/* -----------------------------------------------------------------------
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>

#include <wiringPi.h>

#define MAX_SAMPLES 1024

int sample_micros[MAX_SAMPLES];
int first_sample_level;

#define IR_PIN 7

char buf[128];

int recordPinChanges(int pin, int timeout)
{
   struct timeval now, pulse;
   int sample=0;
   int micros;
   int level;

   first_sample_level = digitalRead(pin);
   level = first_sample_level;

   while (1)
   {
      micros=0;
      gettimeofday(&pulse, NULL);
      while (digitalRead(pin) == level)
      {
         gettimeofday(&now, NULL);
         if (now.tv_sec > pulse.tv_sec) micros = 1000000L; else micros = 0;
         micros = micros + (now.tv_usec - pulse.tv_usec);
         if (micros > timeout) return sample;
      }
      
      if (level)
      {
         level=0;
      }
      else
      {
         level=1;
          micros = -micros;
      }
      sample_micros[sample] = micros;
      if (++sample >= MAX_SAMPLES) break;
   }
   return sample;
}

int main (int argc, char *argv[])
{
   int samples;
   int c;

   if (wiringPiSetupGpio () == -1)
   {
      fprintf (stderr, "Can't initialise wiringPi: %s\n", strerror (errno)) ;
      return 1 ;
   }

   pinMode(IR_PIN, INPUT);
 
   while (1)
   {
      samples = recordPinChanges(IR_PIN, 1000000);
      for (c=0; c<samples; c++)
      {
         printf("%d ", sample_micros[c]);
      }
      printf("\n");
      fflush(NULL);
   }
}

User avatar
ScottBouch
Posts: 146
Joined: Wed Jun 06, 2012 12:32 pm
Location: Midlands, UK
Contact: Website

Re: GPIO Data Logger

Sat Oct 06, 2012 12:05 pm

Hi Joan,

Thank you for sharing your code, i wonder if i can adapt it for all GPIO pins?

Would you be kind enoigh to give me a little guidance on how to use it? I'm new to programming... Is this code to be run using python?

Do your #'s mean the commands are commented out? Should they be removed?

Which part of the code specifies which IO pin? And which specifies the output file, timing etc..

Many thanks, Scott
http://www.scottbouch.com

http://www.youtube.com/user/sbscottmonkey - - - YouTube Channel

User avatar
joan
Posts: 13621
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: GPIO Data Logger

Sat Oct 06, 2012 12:18 pm

The code is written in C and only monitors one pin.

What (I think) you want is reasonably straightforward. If you are in no particular hurry I will write a program to meet your needs. Probably not before Monday though.

User avatar
ScottBouch
Posts: 146
Joined: Wed Jun 06, 2012 12:32 pm
Location: Midlands, UK
Contact: Website

Re: GPIO Data Logger

Sat Oct 06, 2012 1:31 pm

joan wrote:The code is written in C and only monitors one pin.

What (I think) you want is reasonably straightforward. If you are in no particular hurry I will write a program to meet your needs. Probably not before Monday though.
Hi Joan,

Thank you, I really appreciate any help you can give! I really appreciate your kind offer of help!

I'll get to work putting together an op-amp buffer circuit, or perhaps opto-isolated... Need to poke around the boiler circuits to check what voltages are present, hopefully no 240Vac....

Thanks again, Scott..
http://www.scottbouch.com

http://www.youtube.com/user/sbscottmonkey - - - YouTube Channel

jamesh
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 20942
Joined: Sat Jul 30, 2011 7:41 pm

Re: GPIO Data Logger

Sat Oct 06, 2012 2:12 pm

Here's a quick hack to monitor whatever pins you want to. I've not put any time stuff in to make it time out or print the time of level changes, or indeed even compiled it but should give some idea.

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>

#include <wiringPi.h>
 
// cant remmeber exactly how many GPIO's there are! Lesson for the reader..
#define MAX_PINS 20

void MonitorPins(int num_pins, int *pin_ids, int max_samples)
{
   int pin_state[MAX_PINS]; // cannot have more pins than GPIO's
   int samples = 0;
   
   if (num_pins > MAX_PINS-1).
     return;
   
   // Set state array to current pin statesas a default
   for (int i=0;i<num_pins;i++)
   {
     int pin = pin_ids[i];
     
     pinMode(pin, INPUT);
     
     pin_state[pin] = digitalRead(pin);
   }

   while (samples < max_samples)
   {
      // Now monitor
      for (int i=0;i<num_pins;i++)
      {
         int pin = pin_ids[i];
         
         int state = digitalRead(pin);
         
         // Have we changed?
         if (state != pin_state[pin])
         {
            printf("pin %d changed to state %d", pin, state);
            pin_state[pin] = state;
            samples++;
         }
      }
   }  
}

main()
{
   int pins_to_monitor[] = {0,1,2,3,4,5,6,7,8};
   int num_pins = sizeof(pins_to_monitor)/sizeof(int);
   int max_samples = 100;
   
   if (wiringPiSetupGpio () == -1)
   {
      printf ("Can't initialise wiringPi");
      exit();
   }
   
   MonitorPins(num_pins, pins_to_monitor, max_samples);
}

Principal Software Engineer at Raspberry Pi (Trading) Ltd.
Please direct all questions to the forum, I do not do support via PM.

User avatar
joan
Posts: 13621
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: GPIO Data Logger

Sat Oct 06, 2012 3:03 pm

I am planning to be even lazier.

I plan to cut down wiringpi.c of the wiring pi library to just map the gpio memory.

I then plan to read, at a user specified rate, both long words which give the gpio input state. From memory there are 2 32-bit words which give each of the 64 possible gpio digital states. Currently I think all the user accessible gpio pins are mapped to the first 32-bit word.

I plan to log 16 hex digits (representing the 64 gpio bits) with (gettimeofday) timestamp at each state change.

User avatar
joan
Posts: 13621
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: GPIO Data Logger

Sun Oct 07, 2012 8:38 pm

This is very much work in progress but I wanted to put a stake in the ground.

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 GPIO_PINS 54

#define PAGE_SIZE  4096
#define BLOCK_SIZE 4096

volatile uint32_t *gpio ;

uint32_t ignore1, ignore2;

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(-2);
  }

  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(-3);
  }
}

void displayBits(uint32_t b1, uint32_t b2)
{
   char buf[GPIO_PINS];
   int i;

   for (i=0; i<32; i++)
   {
      if (b1 & (1<<i)) buf[i]='1'; else buf[i]='0';
   }

   for (i=0; i<22; i++)
   {
      if (b2 & (1<<i)) buf[i+32]='1'; else buf[i+32]='0';
   }

   buf[GPIO_PINS]=0;

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

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

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

   struct timeval now;
   struct tm tmp;

   gettimeofday(&now, NULL);

   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);
   }

   printf("%s.%06d ", buf, now.tv_usec);
}

int initOpts(int argc, char *argv[])
{
   int opt, usecs, ignore;

   usecs = 0;

   ignore1=0xFFFFFFFF;
   ignore2=0x00000000;

   while ((opt = getopt(argc, argv, "i:t:")) != -1)
   {
      switch (opt)
      {
         case 'i':
            ignore = atoi(optarg);
            if ((ignore >= 0) && (ignore <= 53))
            {
               if (ignore < 32) ignore1 ^= (1<<ignore);
               else ignore2 ^= (1<<(ignore-32));
            }
            break;

        case 't':
            usecs = atoi(optarg);
            break;
        default: /* '?' */
            fprintf(stderr, "Usage: %s [-t usecs] [-i pin(0-53)]\n", argv[0]);
            exit(-4);
        }
    }

}

int main(int argc, char *argv[])
{
   uint32_t reg13, reg14, t13, t14;

   initOpts(argc, argv);

   initGpio();

   reg13 = (*(gpio + 13)) & ignore1;
   reg14 = (*(gpio + 14)) & ignore2;
 
   while(1)
   {
      t13 = (*(gpio + 13)) & ignore1;
      t14 = (*(gpio + 14)) & ignore2;

      if ((t13!=reg13) || (t14!=reg14))
      {
         reg13=t13;
         reg14=t14;
         timestamp();
         displayBits(reg13, reg14);
      }
   }
}
Build using

cc -o gpio-monitor gpio-monitor.c

Run using

sudo ./gpio-monitor

Example output when monitoring an infra red receiver connected to gpio7.
2012-10-07 20:29:39.153763 111100101000001110000000000010000000000000000000000000
2012-10-07 20:29:39.162806 111100111000001110000000000010000000000000000000000000
2012-10-07 20:29:39.167249 111100101000001110000000000010000000000000000000000000
2012-10-07 20:29:39.167852 111100111000001110000000000010000000000000000000000000
2012-10-07 20:29:39.168384 111100101000001110000000000010000000000000000000000000
2012-10-07 20:29:39.168975 111100111000001110000000000010000000000000000000000000
2012-10-07 20:29:39.169494 111100101000001110000000000010000000000000000000000000
2012-10-07 20:29:39.170097 111100111000001110000000000010000000000000000000000000
2012-10-07 20:29:39.170614 111100101000001110000000000010000000000000000000000000
2012-10-07 20:29:39.171217 111100111000001110000000000010000000000000000000000000
2012-10-07 20:29:39.171746 111100101000001110000000000010000000000000000000000000
2012-10-07 20:29:39.172330 111100111000001110000000000010000000000000000000000000
2012-10-07 20:29:39.172844 111100101000001110000000000010000000000000000000000000
2012-10-07 20:29:39.173472 111100111000001110000000000010000000000000000000000000
2012-10-07 20:29:39.173978 111100101000001110000000000010000000000000000000000000
2012-10-07 20:29:39.174572 111100111000001110000000000010000000000000000000000000
2012-10-07 20:29:39.175102 111100101000001110000000000010000000000000000000000000
2012-10-07 20:29:39.175700 111100111000001110000000000010000000000000000000000000
2012-10-07 20:29:39.176220 111100101000001110000000000010000000000000000000000000
2012-10-07 20:29:39.176823 111100111000001110000000000010000000000000000000000000
2012-10-07 20:29:39.178466 111100101000001110000000000010000000000000000000000000
sudo is required as raw access is being made to memory. Such access is normally not allowed as Linux is a multi-user system and you could corrupt other users memory.

By default the current code ignores the second bank of gpio pins (gpios 32-53).

The only implemented option is -i n which changes the ignore state of gpio number n

For masochists try

sudo ./gpio-monitor -i 48

A fraction of a second using the above command.
2012-10-07 20:29:10.134963 111100111000001110000000000010000000000000000000100000
2012-10-07 20:29:10.135805 111100111000001110000000000010000000000000000000000000
2012-10-07 20:29:10.135821 111100111000001110000000000010000000000000000000100000
2012-10-07 20:29:10.135927 111100111000001110000000000010000000000000000000000000
2012-10-07 20:29:10.136021 111100111000001110000000000010000000000000000000100000
2012-10-07 20:29:10.136146 111100111000001110000000000010000000000000000000000000
2012-10-07 20:29:10.136156 111100111000001110000000000010000000000000000000100000
2012-10-07 20:29:10.136271 111100111000001110000000000010000000000000000000000000
2012-10-07 20:29:10.136646 111100111000001110000000000010000000000000000000100000
gpio48 appears to be a 10 microsecond clock.

As written the code can't keep up with a 10 microsecond cycle (it does if the output is changed to hex rather than binary).

The code currently executes a busy loop. I plan to add an optional delay.

User avatar
ScottBouch
Posts: 146
Joined: Wed Jun 06, 2012 12:32 pm
Location: Midlands, UK
Contact: Website

Re: GPIO Data Logger

Tue Oct 09, 2012 10:56 pm

Hi Joan,

The parts for my interface board have started arriving...

I'll try out your 'code so far' hopefully at some point this week.

My interface circuit is:

240Vac - full wave rectifier - resistance - opto isolator - Pi GPIO
http://www.scottbouch.com

http://www.youtube.com/user/sbscottmonkey - - - YouTube Channel

User avatar
joan
Posts: 13621
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: GPIO Data Logger

Wed Oct 10, 2012 11:11 am

No problem. I've been sidetracked so have only done a little more. I'll probably have time to tidy up the code by the time you assemble your parts.

User avatar
joan
Posts: 13621
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: GPIO Data Logger

Sat Oct 13, 2012 10:17 am

I've massaged the code into shape and it's ok for my needs.

Save the code as gpio-monitor.c and build using

cc -o gpio-monitor gpio-monitor.c

Run using

sudo ./gpio-monitor -m gpios_to_monitor -s sample_rate

e.g. sudo ./gpio-monitor -m 0-7 -s 100

It's intended to monitor rather than alter states but you may need to set a gpio as an input. To do so use the -i option.

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), "%[email protected]%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 %[email protected]%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);
   }
}

User avatar
ScottBouch
Posts: 146
Joined: Wed Jun 06, 2012 12:32 pm
Location: Midlands, UK
Contact: Website

Re: GPIO Data Logger

Sat Oct 13, 2012 10:51 pm

Wow, thank you for putting in this effort..

My bridge rectifiers have arrived, as have the DIL sockets, just waiting on the opto isolators now...

I've never played with C, just learning the Python basics... Looking forward to having a go at getting your code working soon!

Does this code save a file as the log, or present the GPIO states on screen?

Do I need any additional settings / software to enable the GPIO pins?

Cheers, Scott
http://www.scottbouch.com

http://www.youtube.com/user/sbscottmonkey - - - YouTube Channel

User avatar
joan
Posts: 13621
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: GPIO Data Logger

Sat Oct 13, 2012 11:35 pm

The output will go to the screen by default. For logging you would normally redirect the output to a file.

e.g. rather than

sudo ./gpio-monitor -mvalues

use

sudo ./gpio-monitor -mvalues >log

which would save the output in a file named log.

You'd probably direct the output to a screen at first until you were happy with the chosen options and then add the redirect.

There is another method which gives both outputs.

sudo ./gpio-monitor -mvalues | tee log

However the screen would only be refreshed when the file buffer was full (not after each line), so you wouldn't have the reassurance of activity.

Another method is to

sudo ./gpio-monitor -mvalues >log &

which saves the output to a file but also runs the program in the background.

You could then use

tail log

periodically to view the last lines of the log file.

For your purposes you'd probably want to use the -i option to set each of the gpios you decide to use as input pins.

e.g. if you wanted to monitor 5 inputs and you decided to use gpios 7-11 you'd use

sudo ./gpio-monitor -m7-11 -i7-11 > log &

The first few lines after running sudo ./gpio-monitor -m48-53
#0 [email protected]:30:19
#1 Sample rate (Hz): 100
#2 Sample interval (microseconds): 10000
#3 Trim date/time characters: 0
#4 Seconds decimal places: 2
#5 Monitoring gpios: 48 49 50 51 52 53
#6 Also displaying gpios: NONE
#7 Setting gpios as inputs: NONE
#8 gpios display order: 48 49 50 51 52 53
#9
#A
#B
#C
#D
#E
#F
[email protected]:30:20.03 011111
[email protected]:30:20.05 111111
[email protected]:30:20.08 011111
[email protected]:30:20.09 111111
[email protected]:30:20.11 011111
[email protected]:30:20.13 111111
[email protected]:30:20.14 011111

User avatar
ScottBouch
Posts: 146
Joined: Wed Jun 06, 2012 12:32 pm
Location: Midlands, UK
Contact: Website

Re: GPIO Data Logger

Sun Oct 14, 2012 7:33 am

Thank you again for the guidance, as well as the code!

It makes a lot of sense now!

Does this run happily in a terminal in X? Or should I run it at the prompt before starting X?

Thank you again, Scott
http://www.scottbouch.com

http://www.youtube.com/user/sbscottmonkey - - - YouTube Channel

User avatar
ScottBouch
Posts: 146
Joined: Wed Jun 06, 2012 12:32 pm
Location: Midlands, UK
Contact: Website

Re: GPIO Data Logger

Sun Oct 14, 2012 10:17 am

How are you reaching 50 odd IO? Thought the Pi only had 8 pins for GPIO, or are you using the Gert Board?

Cheers Scott
http://www.scottbouch.com

http://www.youtube.com/user/sbscottmonkey - - - YouTube Channel

User avatar
joan
Posts: 13621
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: GPIO Data Logger

Sun Oct 14, 2012 11:27 am

ScottBouch wrote:How are you reaching 50 odd IO? Thought the Pi only had 8 pins for GPIO, or are you using the Gert Board?

Cheers Scott
See http://elinux.org/Rpi_Low-level_peripherals

The Pi has 54 gpio pins of which 17 (gpios 0, 1, 4, 7, 8, 9, 10, 11, 14, 15, 17, 18, 21, 22, 23, 24, 25) are accessible on P1 and 4 (gpios 2, 3, 5, 27) are accessible on S5. They can all be potentially used for user I/O. Most have a primary use for a specialised purpose, e.g. I2C, SPI, UART which leaves the 8 currently referred to.

For instance I never use SPI so often use P1-19, 21, 23 as they are next to each other (convenient for cabling 3 pin headers).

The levels of the GPIO pins are memory mapped to two 32 bit words which I think of as bank1 and bank2. These are located at the gpio memory mapped base address plus 13*4, and 14*4 bytes respectively.

The level of gpio pin x is present in bank1 for gpios 0-31, and in bank2 for gpios 32-53.

For instance this code snippet will find the current levels for gpios 5 and 33.
volatile uint32_t *gpio ;

uint32_t bank1, bank2;

#define BANK1 13
#define BANK2 14
...
code to map gpio to hardware base address elided
...
bank1 = *(gpio + BANK1);
bank2 = *(gpio + BANK2);
...
int pin5level, pin34level;

if (bank1 & (1<<5)) pin5level=1; else pin5level=0;
if (bank2 & (1<<2)) pin34level=1; else pin34level=0;
In C & is a bit AND operation and << is a bit left shift operation.

As an aside gpio48 appears to be used internally as a 10 microsecond clock for the SD card. It is useful as a quick check to use the program.

e.g. sudo ./gpio-monitor -m48

huskybloke
Posts: 2
Joined: Sat Oct 13, 2012 7:26 am
Location: Essex

Re: GPIO Data Logger

Wed Oct 17, 2012 9:19 pm

Hi
just a thought but if the signal to be monitored is full wave rectified mains then it will effectively be at 100hz . Will this be a problem when measuring fast changes?

User avatar
ScottBouch
Posts: 146
Joined: Wed Jun 06, 2012 12:32 pm
Location: Midlands, UK
Contact: Website

Re: GPIO Data Logger

Sun Oct 28, 2012 11:15 am

huskybloke wrote:Hi
just a thought but if the signal to be monitored is full wave rectified mains then it will effectively be at 100hz . Will this be a problem when measuring fast changes?
Yes, you're right.. I'll be using some small capacitors to smooth out the signal between the rectifiers and Opto. I'm also including a Zener diode after the rectifier and resistors to make a little voltage regulator prior to the Opto. This will clip off the tops of the waves, leaving very short troughs.. so the capacitor can be very small. Also I wan to limit the size of the capacitor, as it could mean I miss when a signal drops off momentarily - so it's a fine balancing act...

I've started to put my circuit together now most of the bits have arrived, hopefully will be having a go at testing this code today

Cheers, Scott.
http://www.scottbouch.com

http://www.youtube.com/user/sbscottmonkey - - - YouTube Channel

User avatar
ScottBouch
Posts: 146
Joined: Wed Jun 06, 2012 12:32 pm
Location: Midlands, UK
Contact: Website

Re: GPIO Data Logger

Sun Oct 28, 2012 6:20 pm

Hi, I'm having a go at this now... having a little issue, (most probably my inexperience) so have tried to write the following in as much detail as I can:

I've started off with a fresh install of Raspbian Wheezy (downloaded today - 28-10-2012), I'm currently in X and using and Midori to browse this forum.

I copied and pasted your code into LeafPad (version 0.8.18.1) and saved as gpio-monitor.c in location /home/pi

I then opened up an LXTerminal (version 0.1.11), typed:

Code: Select all

[email protected] ~ $ cc -o gpio-monitor gpio-monitor.c
... and got many lines of:

Code: Select all

gpio-monitor.c:446:1: error: stray ‘\240’ in program
gpio-monitor.c:446:1: error: stray ‘\302’ in program
gpio-monitor.c:446:1: error: stray ‘\240’ in program
gpio-monitor.c:446:1: error: stray ‘\302’ in program
gpio-monitor.c:446:1: error: stray ‘\240’ in program
gpio-monitor.c:447:1: error: stray ‘\302’ in program
gpio-monitor.c:447:1: error: stray ‘\240’ in program
gpio-monitor.c:447:1: error: stray ‘\302’ in program
gpio-monitor.c:447:1: error: stray ‘\240’ in program
Not to be put off, I had a go at running it... I typed:

Code: Select all

[email protected] ~ $ sudo ./gpio-monitor -m0-5
...and got:

Code: Select all

sudo: ./gpio-monitor: command not found
So I thought I'd try the logger... I typed:

Code: Select all

[email protected] ~ $ sudo ./gpio-monitor -m0-5 >log
...and got the same response, however, the log (0 bytes plain text document) file was created in /home/pi!

Code: Select all

sudo: ./gpio-monitor: command not found
Am I doing something daft? do I need anything installed (above what comes with the standard Wheezy build) to use C code or the GPIO?

Thanks again, Scott
http://www.scottbouch.com

http://www.youtube.com/user/sbscottmonkey - - - YouTube Channel

User avatar
joan
Posts: 13621
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: GPIO Data Logger

Sun Oct 28, 2012 7:05 pm

I've not used LeafPad. Those seem to be strange non-Acscii characters which seem to have been appended to the source code.

Rather than LeafPad could you use nano from a command prompt and copy paste into its window. Then save.

If the program compiles correctly you would see no screen output during compilation. Doing ls -l gpio-monitor should show the file.

User avatar
ScottBouch
Posts: 146
Joined: Wed Jun 06, 2012 12:32 pm
Location: Midlands, UK
Contact: Website

Re: GPIO Data Logger

Sun Oct 28, 2012 7:31 pm

Hi Joan,

I just erased my Leaf Pad file, and tried again using Nano, and got the same errors when trying to build with cc.

Could it be the fact that I'm copying / pasting the text from this forum that characters may get screwed up?

Thanks again for the additional help, Scott.
http://www.scottbouch.com

http://www.youtube.com/user/sbscottmonkey - - - YouTube Channel

User avatar
joan
Posts: 13621
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: GPIO Data Logger

Sun Oct 28, 2012 7:52 pm

I just tried myself and it compiled OK. Each line has four spaces inserted at the line start but they do not effect the compilation.

I did a code SELECT ALL, a control C, and then a paste into nano. I chose to save mine as z.c and then did
[email protected]:~$ cc z.c
[email protected]:~$ ./a.out
Unable to open /dev/mem: Permission denied
[email protected]:~$ sudo ./a.out
#0 [email protected]:46:42
#1 Sample rate (Hz): 100
#2 Sample interval (microseconds): 10000
#3 Trim date/time characters: 0
#4 Seconds decimal places: 2
#5 Monitoring gpios: NONE
#6 Also displaying gpios: NONE
#7 Setting gpios as inputs: NONE
#8 gpios display order: NONE
#9
#A
#B
#C
#D
#E
#F
^[email protected]:~$

rgh
Posts: 211
Joined: Fri Nov 25, 2011 3:53 pm

Re: GPIO Data Logger

Sun Oct 28, 2012 8:10 pm

ScottBouch wrote:Hi Joan,

I just erased my Leaf Pad file, and tried again using Nano, and got the same errors when trying to build with cc.

Could it be the fact that I'm copying / pasting the text from this forum that characters may get screwed up?

Thanks again for the additional help, Scott.
Those characters are 0xA0 0xC2 and represent a "non-blanking space" in UTF-8. In HTML you use &nbsp; to force white space, and it seems this forum uses those when displaying

Code: Select all

 sections (not very clever of it; that is what <pre> is for).  In fact for a run of white space it seems to use alternating ASCII space (0x20) and UTF-8 non-blanking space, as you can see if you tell your browser to display the page source.

Someone had this problem cutting and pasting a script from the servoblaster thread too; I ended up emailing my copy of the script that worked.  I cut and paste from the forum in to a vim session in an xterm, on an x86 desktop running Ubuntu 11.10 (iirc), and I don't get a problem.  In my case the &nbsp; get munged to regular spaces somewhere along the way.  So you could try installing xterm, running that, then running vim in the xterm, but it might not help - it might be you need to change something in your environment to avoid the problem.  My xterm is a regular one, not a utf-8 aware one, I think.

The best solution would be for you to retype the code yourself, that way you'll learn some more C along the way as you make typos and have to fix them :)  Or you could ask joan to email the code to you..

Return to “Automation, sensing and robotics”