text: /*
   * grrl_bat_mon.c
   *
   * A bettery monitor for the Adafruit Pocket PiGRRL
   *
   * FRAMEBUFFER DISLAY WORK BASED ON :::
   * http://raspberrycompote.blogspot.ie/2013/03/low-level-graphics-on-raspberry-pi-part.html
   *
   * Original work by J-P Rosti (a.k.a -rst- and 'Raspberry Compote')
   *
   * Licensed under the Creative Commons Attribution 3.0 Unported License
   * (http://creativecommons.org/licenses/by/3.0/deed.en_US)
   *
   * Distributed in the hope that this will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
   *
   */
  
  #include <unistd.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <signal.h>
  #include <string.h>
  #include <fcntl.h>
  #include <linux/fb.h>
  #include <sys/mman.h>
  #include <sys/ioctl.h>
  #include <wiringPi.h>
  
  // 'global' variables to store screen info
  char *fbp = 0;
  struct fb_var_screeninfo vinfo;
  struct fb_fix_screeninfo finfo;
  int fbfd = 0;
  struct fb_var_screeninfo orig_vinfo;
  long int screensize = 0;
  
  // global triggered when program should exit !
  int stop_program = 0;
  
  // constants for battery icon
  int batt_icon [20][35] = {
          {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
          {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
          {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0},
          {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0},
          {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0},
          {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0},
          {0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0},
          {0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0},
          {0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0},
          {0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0},
          {0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0},
          {0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0},
          {0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0},
          {0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0},
          {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0},
          {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0},
          {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0},
          {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0},
          {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
          {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
  
  int batt_start_x = 10;
  int batt_start_y = 10;
  int batt_fore_colour = 65535;
  int batt_back_colour = 0;
  
  // seconds between checks of the low battery output
  int lbo_refresh = 1;
  // refresh rate of the framebuffer
  int fb_refresh = 10000;
  
  // GPIO pin the low battery output connects too
  int lbo_gpio = 19;
  
  // called when terminating
  void signal_callback_handler(int signum)
  {
  
      // cleanup
      // unmap fb file from memory
      munmap(fbp, screensize);
      // reset the display mode
      if (ioctl(fbfd, FBIOPUT_VSCREENINFO, &orig_vinfo)) {
          printf("Error re-setting variable information.\n");
      }
      // close fb file
      close(fbfd);
      printf("Pocket PiGRRL Battery Monitor Finished (%d).\n",signum);
  
      exit(signum);
  }
  
  // helper function to 'plot' a pixel in given color
  void put_pixel(int x, int y, int c)
  {
      // calculate the pixel's byte offset inside the buffer
      unsigned int pix_offset = x + y * finfo.line_length;
  
      // now this is about the same as 'fbp[pix_offset] = value'
      *((char*)(fbp + pix_offset)) = c;
  
  }
  
  // function to draw a battery to the frame buffer
  void draw_battery(start_x, start_y, fore_colour, back_colour) {
      int x;
      int y;
      for (x = 0; x < 20; x++) {
          for (y = 0; y < 35; y++) {
              if (batt_icon[x][y] == 1) {
                  put_pixel(start_y + y, start_x + x, fore_colour);
              } else {
                  put_pixel(start_y + y, start_x + x, back_colour);
              }
          }
      }
  }
  
  // application entry point
  int main(int argc, char* argv[])
  {
  
      int low_batt = 0;
      int count = 0;
  
      printf("Pocket PiGRRL Battery Monitor Running.\n");
  
      // Open the framebuffer file for reading and writing
      fbfd = open("/dev/fb1", O_RDWR);
      if (fbfd == -1) {
          printf("Error: cannot open framebuffer device.\n");
          return(1);
      }
  
      // Get variable screen information
      if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) {
          printf("Error reading variable information.\n");
      }
  
      //DEBUG info from framebuffer
      /*
      printf("Original %dx%d, %dbpp\n", 
              vinfo.xres, vinfo.yres, 
              vinfo.bits_per_pixel );
      */
  
      // Store for reset (copy vinfo to vinfo_orig)
      memcpy(&orig_vinfo, &vinfo, sizeof(struct fb_var_screeninfo));
  
      // Change variable info
      vinfo.bits_per_pixel = 8;
      if (ioctl(fbfd, FBIOPUT_VSCREENINFO, &vinfo)) {
          printf("Error setting variable information.\n");
      }
  
      // Get fixed screen information
      if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)) {
          printf("Error reading fixed information.\n");
      }
  
      // map fb to user mem 
      screensize = finfo.smem_len;
      fbp = (char*)mmap(0, 
                screensize, 
                PROT_READ | PROT_WRITE, 
                MAP_SHARED, 
                fbfd, 
                0);
  
      if ((int)fbp == -1) {
          printf("Failed to mmap.\n");
      }
      else {
  
          // Register signal and signal handler for cleanup
          signal(SIGINT, signal_callback_handler);
          signal(SIGTERM, signal_callback_handler);
  
          // setup gpio
          wiringPiSetupGpio();
          pinMode(lbo_gpio, INPUT);
          pullUpDnControl(lbo_gpio, PUD_UP);
  
          // inifinite loop
          for(;;) {
              low_batt = digitalRead(lbo_gpio);
              count = 0;
              while(low_batt == 0) {
                  // draw battery icon
                  draw_battery(batt_start_x, batt_start_y, batt_fore_colour, batt_back_colour);
                  usleep(fb_refresh);
                  // check for low batt every second
                  if (count > (1000000 / fb_refresh)) {
                      low_batt = digitalRead(lbo_gpio);
                      count = 0;
                  }
                  count = count + 1;
              }
              sleep(lbo_refresh);
          }
      }
  
      return 0;
  
  }
  