Tuesday 12 September 2017

Internet of Things: Solar Battery Voltmeter

Get solar battery voltage now

From the last post, we have a car battery being charged by a solar panel, and to monitor the battery voltage we have a Raspberry Pi with a WiPi (USB WiFi dongle for the Pi) with an added USB serial port dongle hacked as a voltmeter.

Solar panel charging a car battery


Quick, basic way to present information in the Internet of Things


Now I can log into my Pi remotely through wifi:

ssh -t 172.16.1.10

A python program can be written very quickly, say solarbat.py:
#!/usr/bin/python
import serial
from time import localtime, strftime

port=serial.Serial('/dev/ttyACM0', timeout=1)

if __name__ == "__main__":

  port.write('A')
  Vbat= port.read(10)
  Battery_Voltage=int(format(ord(Vbat[0]), '02x'), 16)*256 + \
                  int(format(ord(Vbat[1]), '02x'), 16)
  if Vbat[2:5] != 'HCM':
    print 'Checkbyte Fail' 

# Calibrated_Battery_Voltage = Battery_Voltage * 12.56 / 640
  Calibrated_Battery_Voltage = Battery_Voltage * 13.75 / 689

  print Calibrated_Battery_Voltage,'Volts',strftime("%Y-%m-%d %H:%M:%S", localtime())


When executed:
root@piface1:/home/heong/analog_pic# ./solarbat.py
12.893625 Volts 2017-09-06 13:48:55

Technically speaking it is an Internet of Things device now. I have set up my modem router to host my website www.cmheong.com, and if you logged into my webhost, you could now remotely trigger the battery voltage measurement and retrieve the result:

$ ssh -t root@172.16.1.10 /home/heong/analog_pic/solarbat.py
root@172.16.1.10's password:
12.91325 Volts 2017-09-06 13:52:24
Connection to 172.16.1.10 closed.

A bash script can be set up in the webserver to log in automatically, execute solarbat.py and retrieve the results. What bash can do, an Android smartphone can. 

And there you have it, a first cut of an IoT Solar Battery meter.
It is more common to expose this functionality via a network socket, and for this to happen we need two more programs, a server program to run solarbat.py on the Pi, and to forward the results to the webserver.

The webserver runs a client program using a PHP script. The script runs client program to extract and displays the results.

I got some sample code from here (honestly I have never done this before and all it took was a couple of hours). The client code is unmodified:

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h> 

int main(int argc, char *argv[])
{
    int sockfd = 0, n = 0;
    char recvBuff[1024];
    struct sockaddr_in serv_addr; 

    if(argc != 2)
    {
        printf("\n Usage: %s <ip of server> \n",argv[0]);
        return 1;
    } 

    memset(recvBuff, '0',sizeof(recvBuff));
    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("\n Error : Could not create socket \n");
        return 1;
    } 

    memset(&serv_addr, '0', sizeof(serv_addr)); 

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(5000); 

    if(inet_pton(AF_INET, argv[1], &serv_addr.sin_addr)<=0)
    {
        printf("\n inet_pton error occured\n");
        return 1;
    } 

    if( connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
    {
       printf("\n Error : Connect Failed \n");
       return 1;
    } 

    while ( (n = read(sockfd, recvBuff, sizeof(recvBuff)-1)) > 0)
    {
        recvBuff[n] = 0;
        if(fputs(recvBuff, stdout) == EOF)
        {
            printf("\n Error : Fputs error\n");
        }
    } 

    if(n < 0)
    {
        printf("\n Read error \n");
    } 

    return 0;
}

The server code I modified to invoke solarbat.py when a client request triggers it:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <time.h> 

int readfile(char *buffer, int maxsize)
{
  FILE *fileread = NULL;
  char *filename = "solarbat.txt";
  unsigned int i = 0;

  fileread = fopen(filename, "r");

  if(fileread == NULL)
  {
    int saved_errno=errno;
    if(saved_errno == ENOENT)
      printf("File %s does not exist\n", filename);
    else
      printf("errno is %d\n", saved_errno);
    buffer[0]=0;
    return 0;
  }
  else
  {
    // printf("%s successfully opened, reading file ...\n", filename);
    while(!feof(fileread)) // read only the last line
      i=fread(buffer, 1, maxsize, fileread);
    if (i<maxsize) // terminate string for printf
        buffer[i] = 0;
    else
        buffer[maxsize]=0;
    fclose(fileread);
    return i;
  }
}

int main(int argc, char *argv[])
{
    int listenfd = 0, connfd = 0;
    struct sockaddr_in serv_addr; 

    char sendBuff[1025];
    char readBuff[1025]; // cmheong 2017-09-11

    time_t ticks; 

    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    memset(&serv_addr, '0', sizeof(serv_addr));
    memset(sendBuff, '0', sizeof(sendBuff)); 

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(5000); 

    bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); 

    listen(listenfd, 10); 

    while(1)
    {
        connfd = accept(listenfd, (struct sockaddr*)NULL, NULL); 

        system("/home/heong/analog_pic/solarbat.py > ./solarbat.txt");
        readfile(readBuff, 1024);

        snprintf(sendBuff, sizeof(sendBuff), "%s", readBuff);
        write(connfd, sendBuff, strlen(sendBuff)); 

        close(connfd);
        sleep(1);
    }
}

To compile, you do (in Pi):
gcc -o solar_server solar_server.c

Over at your web server you do:
gcc -o client client.c

This seems a bit pedantic but it takes care of the different systems- the Pi is an ARM system and the webhost can be an Intel-based blade server.

To execute, you run on the Pi:
 ./solarbat_server

Over on client you do (assuming 172.16.1.10 is the address of the Pi):
 ./client 172.16.1.10
Mon Sep 11 14:15:45 2017

The last step is the php script. solarbat.php

<!DOCTYPE html>
<html>
<body>

<?php
echo "<h2>Solar Battery Voltage</h2>";
$last_line = system('/home/heong/socket/client 172.16.1.10', $retval);
//echo $last_line;
?> 

</body>
</html>

Now to get the solar battery measurement you only need to aim your browser at the script:

http://www.cmheong.com/solar/solarbat.php

Should get you this screenshot:


The Internet of Things is that simple. Happy Trails

Update Thu 14 Sep 2017: yesterday the weather was overcast a good part of the afternoon (the panel faced west) and the battery did not charge up properly. Over the night, the Raspberry Pi completely discharged the battery and only restarted the morning of the 14th when the sun came up. The php link stopped working - my apologies.

The voltmeter readings themselves told the story:


No comments:

Post a Comment