Monday 25 September 2017

Bluetooth IoT Solar Battery Voltmeter

A man is like Bluetooth , he's connected to you when you're nearby, but search for others when you're far away.

A woman is like Wi-Fi , she notices all the available ones but connect to the strongest.
But remember, Bluetooth will stick close to you because it's made that way.


The Arduino HC-06 is an amazingly inexpensive Bluetooth module. It may actually be better to buy the HC-05 as it can be set to master or slave. The HC-06 commonly comes in slave configuration (technically the HC-06-S. The master is HC-06-M). It is serial port(UART) to Bluetooth adapter. I bought the HC-06 slave because it has fewer settings to worry about. The master will be my laptop bluetooth module.



Rather inconveniently its TTL level is 3.3V, while my USB RS-485 adapter is 5V TTL. I could have swapped in a 3.3V PIC18LF14K50 for a perfect fit, but it meant junking a perfectly good 5V PIC18F14K50. It is simple enough to use a resistor divider:
  
Notice that the resistive divider is only necessary for the PIC18F14K50's TX pin. A 5V TTL device will read a 3.3V signal from the HC-06 but the converse will damage the HC-06.

For test purposes I used an unmodified USB to RS485 board. I removed the RS-485 IC, the MAX485E so that it would not interfere with the HC-06. The latter is powered from the 5V line of my USB hub. Connect up 2 lines for 5V and 0V, RX of HC-06 to TX of the PIC18G14K50, and TX of HC-06 to RX of PIC18F14K50 and plug right in:
CH340G USB To TTL Converter

You can buy a USB to TTL serial dongle online for RM12.88.the CH340G USB to TTL adapter . It even has a selector for 5V/3V operation so you don't have to mess about with resistor dividers. 

USB RS485 dongle with Bluetooth HC-06 module. Notice the current is still 10mA.
With Bluetooth active it is 20mA
When powered on successfully, the HC-06 blinks red.

Next we whip out our laptop running Linux (I use Slackware) and invoke hciconfig:
root@aspireE1:/home/heong$hciconfig
hci0:   Type: BR/EDR  Bus: USB
        BD Address: A4:DB:30:55:9C:AA  ACL MTU: 1022:8  SCO MTU: 183:5
        DOWN
        RX bytes:574 acl:0 sco:0 events:30 errors:0
        TX bytes:368 acl:0 sco:0 commands:30 errors:0

If you get nothing at this stage, you do not have a bluetooth controller, but if you get output like that above you go on to:

root@aspireE1:/home/heong$hciconfig hci up

If you check again your controller should read 'UP':

root@aspireE1:/home/heong$hciconfig
hci0:   Type: BR/EDR  Bus: USB
        BD Address: A4:DB:30:55:9C:AA  ACL MTU: 1022:8  SCO MTU: 183:5
        UP RUNNING
        RX bytes:1148 acl:0 sco:0 events:60 errors:0
        TX bytes:736 acl:0 sco:0 commands:60 errors:0

Next we use bluetoothctl:

root@aspireF15:/home/heong$bluetoothctl
[NEW] Controller C8:FF:28:27:7D:2C BlueZ 5.40 [default]
[NEW] Device 98:D3:32:30:BE:15 HC-06

If you do not see the last line with HC-06, it just means it is your bluetooth module has not been turned on. If your computer does not have bluetooth, a USB bluetooth dongle is very cheap. To turn on your bluetooth, simply do (while still in bluetoothctl). 

Note that some laptops may require to press a button or a keyboard combination (Acer used to have Fn-F3).

[bluetooth]# power on
[CHG] Controller C8:FF:28:27:7D:2C Class: 0x00010c
Changing power on succeeded
[CHG] Controller C8:FF:28:27:7D:2C Powered: yes

If it still does not come up then do:

bluetooth]# scan on
Discovery started
[CHG] Controller C8:FF:28:27:7D:2C Discovering: yes
[CHG] Device 98:D3:32:30:BE:15 LegacyPairing: yes

Next you launch an agent to handle the authentication:

[bluetooth]# agent on
Agent registered
[bluetooth]# default-agent
Default agent request successful

Next you pair your laptop bluetooth module with the HC-06:

[bluetooth]# pair 98:D3:32:30:BE:15
Attempting to pair with 98:D3:32:30:BE:15
[CHG] Device 98:D3:32:30:BE:15 Connected: yes
Request PIN code
[agent] Enter PIN code: 1234
[CHG] Device 98:D3:32:30:BE:15 UUIDs: 00001101-0000-1000-8000-00805f9b34fb
[CHG] Device 98:D3:32:30:BE:15 Paired: yes
Pairing successful

If the pairing has been successful, the HC-06 blinkenlight will be just on, and no longer blinking.

Next I used rfcomm to create a device file /dev/rfcomm0:

rfcomm bind /dev/rfcomm0 98:D3:32:20:BB:7B 1

At this point you are ready for the final test. You need minicom, a serial terminal emulator. Launch 2 instances on 2 consoles (I use KDE's konsole). Aim one minicom at the bluetooth device /dev/rfcomm0 and the other at /dev/ttyUSB0 (usually, but your usage may vary depending on your USB serial dongle). Set the baurate in both minicom to 9600 baud, 8 bits, 1 stop, no parity. Flow control is None.

Type into each minicom console and watch the results appear in the other. Repeat for each console as you need to check both TX and RX.

And there you have it, an HC-06 sending (and receiving) data. In the next post we shall connect the HC-06 to the IoT Solar Battery Voltmeter, so stay tuned.


Solar Battery Blues

The IoT Solar Battery Voltmeter project resulted in a dead battery. It now does not accept a charge, and its voltage dropped below 12V very quickly when discharging, indeed when it was not being charged. There is nothing else to do but to replace it.
20 minutes after sundown the battery voltage went from 14V to 10.9V

Now before I get too misleading, it is not a good idea to use a car battery in place of a deep-cycle battery. I started experimenting doing just that around 12 years ago mainly because they cost about half of the deep cycle ones.

3KW sine-wave UPS APC Matrix 3000 hacked to accept 4 36Ah Yuasa 50B24R

The idea was to over-rate the car batteries and hopefully they will then have a worthwhile lifetime and cost. The reason for this is all cars have a residual load on the battery when not in use. This comes from the digital clock, stereo, alarm and the various car engine electronic modules. It is thought the load here should be 85mA for a new model and 50mA for an old model.

So in general you cannot replace a deep-cycle battery with a car battery, unless the load is less than 100mA.

After all these years progress have overtaken the lead acid car battery. I recently bought a 20Ah (at 3.7V) lithium-ion power bank for RM87. This is somewhat comparable to my 36Ah (but at a stonking 12V)  Yuasa at RM195. Now lithium-ion batteries are deep-cycle so they are definitely worth investigating.

This lithium-ion power bank is rated 20Ah25V and costs RM87
After all this is why I built the IoT Solar Battery monitor, to investigate batteries and their chargers.

So, apart from obvious misuse, exactly what went wrong? The load was only 200mA, and Yuasa 50B24R ECO-R GS car battery, it should be more than enough. 50B24R is nominally 36 Ampere-hours. If it completely discharged (again, not a good thing) it could output 3A over the 12 hours of a tropical night.

The actual current draw was 0.2A at 5V. This should translate to 0.083A at 12V. Assuming the 12V to 5V conversion efficiency of 75%, we can downrate it to 0.111A at 12V. At 111mA the battery should last 324 hours or 13 days. So the battery is way over-sized and yet lasted only nine months much less than the expected 3 years lifetime I get from the same battery in my car.

The problem could also be in previous tests on the solar battery I had completely discharged it 3 or 4 times. Now car batteries do not like to be over-discharged and they will fail prematurely. So, the 36Ah Yuasa 50B24R was right there at the edge, and the previous deep discharges may have tipped it over.

A simpler cause can be that the solar panel's charging rate could not keep up with the discharge rate at night, and over a few days the battery progressively got weaker and eventually completely discharged. The solar panel will put out a variable charge depending on the weather and we have had unseasonably rainy weather recently. Indeed the reason to build the IoT Solar Battery Voltmeter was to investigate this.

It could also be that my Gamma solar charge controller was optimized for deep-cycle batteries and somehow it damaged the car battery by overcharging. One clue is that the battery charging voltage is often 14 to 14.8V, clearly over the 13.8V trickle charge limit for a fully-charged battery.

In any case, the IoT Voltmeter consumed too much of the battery charge it was monitoring (this left only 30mA for the load!), and we could do with a leaner voltmeter.

Of course we could power the voltmeter separately using another battery, or even from mains power. Mains power would have been ideal, except we live on top of a little hill that seems to get struck by lightning a lot, and to get the weeks-long data we need, an isolated system like a battery-powered voltmeter seems like a good idea.

Alternatively we can try using inductively-coupled wireless charging like the Qi, often used to charge smartphones.

The Qi wireless charger & receiver combination can deliver some 500mA at 5V, enough to power the Raspberry Pi-based IoT Voltmeter


Indeed we will try both ideas, but it seems wise to try to reduce the power consumption of the Voltmeter. This brings us to the PIC18F14K50-based voltmeter minus the Raspberry Pi. The PIC18F14K50 USB RS485 board I used consumed only 10mA at 5V. This should give me a maximum load of 90mA for a N60 car battery.

The PIC18F14K50 converted to transmit its data via RS-485 takes only 10mA..

Now I can get it to transmit data back using its RS-485 port, indeed I have already done so, but RS-485 involved copper wiring back to my webserver and this broke the mains isolation.

To maintain isolation I need a wireless data transmission method, and it seems like a good idea to try Bluetooth. There is an existing product for sale that will do exactly that, but the price is a little high:

The CTEX CTX bluetooth battery monitor costs RM305

The Arduino HC-06-S bluetooth slave is only RM15

 Besides, rolling your own is not only cheap, but might be fun. Happy trails.

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:


Take your PIC

The Microchip PIC microcontrollers are amazing.  I encountered them back in 1998, and they are game changers for digital electronics. They are cheap (and I mean throwaway cheap), easy to power (they have wide operating voltages, have very versatile I/O and are surprisingly powerful.

One of many incarnations of the PIC24. Photo by Acdx


The 'F' series (PIC16F, PIC18F, PIC24F) were electrically re-programmable. They also have eeprom, which means you can retain parameters and data after power-off. Best of all Microchip provided a free (rare at the time) subset of development tools, including sample code.

Dinosaurs- ultra-violet light erasable PICs. Photo from wikipedia


It now made no sense to use timer ICs like the CD4040, or even the iconic LM555 or multivibrators like the 74121. A PIC is much more precise, does not drift over time, and more consistent, especially for production units. For me, it even replaced small-scale FPGA. For an old assembly language jock (my first computer language was solder!), it was heaven-sent.

The PIC18F14K50 is one of the first USB microcontrollers. It came with a free subset of the MPLAB IDE and you can get it to work with a C compiler. Best of all there is a lot of C source code, for many types of USB Device, USB Hosts and USB OTG. The flash programmer Pickit 2 was cheap, especially the third-party versions.
The USB interface is the current de facto interface standard, and was well worth the investment in time from 15 years ago. Microchip's Low Pin Count Development Kit was cheap and can be deployed with minimal modifications.

Left: USB-RS485 interface 
Right: Low Pin Count Development Kit. 
The USB RS485 dongle is my design, a slightly modified derivative of the Microchip Low Pin Count Development Kit. It is based on the PIC18F14K50 microcontroller. The following work also applies to the Low Pin Count Development Kit. The advantage is that it is compatible with the existing Windows and Linux device drivers: both Windows and Linux will automatically recognize it as a garden-variety USB serial port. We thus avoid the need to write custom device drivers (although writing device drivers can be fun and profitable- perhaps the subject of another post).


PIC18F14K50-based RS485 USB dongle and the PICKIT2 programmer
The intention is to use the USB RS485 dongle, together with the Raspberry Pi to measure the battery voltage at my solar panel. This is gross overkill for an embedded voltmeter, but as we shall see, it expands into an Internet of Things device. And by having the device come up as a standard USB serial port, we again avoid the need for custom Linux and Windows device drivers.

It would be a strange serial port. When sent any valid character, the device will respond with:
byte1 byte2 'HCM' byte3 byte4 'LJP'. 

Where byte1 and byte2 are hexadecimal bytes from AN8 and similarly, byte3 and byte4 are hexadecimal bytes from AN9. The PIC's Analog-to-Digital Converter is 10-bits, so 2 bytes are required for each input.

USB RS485 dongle with added resistor divider circuit

USB RS485 dongle with Raspberry Pi, mounted as an IoT solar battery voltmeter

My version of MPLAB only runs on Windows (you might have better luck with the newer MPLAB X), so I usually run it from my Qemu Virtual Machine. I then copy the compiled hex file and program it using pk2cmd.

Screenshot of Slackware 14.2 Linux running MPLAB 8.3 Windows XP on a Qemu  Virtual Machine

The sample code I am using is the Microchip USB Device - CDC - Serial Emulator. There is an online version here, but I would recommend you download the Microchip version.

The PIC18F14K50 has 9 usable external analog input lines. We only need two, one for the battery and one for the solar panel. We use AN8 and AN9, just because they happen to be unused and easily soldered on the PCB.

We find the file main.c in the sample code and insert the following lines of C code to initialize the PIC:

void InitializeUSART(void)
{
    #if defined(__18CXX) // __18CXX *is* defined
            unsigned char c;
        #if defined(__18F14K50)
            TRISC |= 0x30; // Set up AN8 AN9 for analog in
            ANSELH = 0x03; // Enable AN8-AN9, RB4, RB5
                           // we use RB4 for MAX485 RE (receiver enable)
            ANSEL = 0x00;  //Disables A4-A7 (enables RC0-RC3 for USB LEDs)
            ADCON2 = 0x9D; // Right-justified output, 6TAD
            ADCON1 = 0x00; // Vdd & Vss as +ve & -ve voltage references
            ADCON0 = 0x21; // Select AN8 (CHS=1000) and turn on ADC

Next we find the function ProcessIO() and insert:

#if defined(__18CXX)
    #define mDataRdyUSART() PIR1bits.RCIF
    #define mTxRdyUSART()   TXSTAbits.TRMT
    #define mAN8Busy()   ADCON0bits.GO 
#elif defined(__C30__) || defined(__C32__)
    #define mDataRdyUSART() UART2IsPressed()
    #define mTxRdyUSART()   U2STAbits.TRMT
#endif

void ProcessIO(void)
{
  //Blink the LEDs according to the USB device status
  BlinkUSBStatus();
  // User Application USB tasks
  if((USBDeviceState < CONFIGURED_STATE)||(USBSuspendControl==1)) return;

  if (RS232_Out_Data_Rdy == 0)  // only check for new USB buffer if the old RS232 buffer is
  {               // empty.  This will cause additional USB packets to be NAK'd
    LastRS232Out = getsUSBUSART(RS232_Out_Data,64); //until the buffer is free.
    if(LastRS232Out > 0)
    {
      RS232_Out_Data_Rdy = 1;  // signal buffer full. Any amount of data will do
      RS232cp = 0;  // Reset the current position
      mLED_3_On();  // 2017-09-03 test code
      mLED_4_Off(); // 2017-09-03 test code
    }
    else
    {
      mLED_3_Off();  // 2017-09-03 test code
      mLED_4_Off();  // 2017-09-03 test code
    }
  }

  if(RS232_Out_Data_Rdy && !mAN8Busy() && RS232cp==0 ) // 2017-09-03 Received command, ADC is free
  {
    ADCON0 = 0x21; // 2017-09-03. Select AN8 (CHS=1000) and turn on ADC
    ADCON0bits.GO = 1; // Start conversion
    ++RS232cp;    // Indicate pending conversion
    mLED_3_On();  // 2017-09-03 test code
    mLED_4_On(); // 2017-09-03 test code
  }

  if(RS232_Out_Data_Rdy && RS232cp==1) // 2017-09-03 check if conversion done
  {
    if (!mAN8Busy())
    {
      USB_Out_Buffer[NextUSBOut++] = ADRESH; // Pick up results
      USB_Out_Buffer[NextUSBOut++] = ADRESL; // Pick up results
      USB_Out_Buffer[NextUSBOut++] = 'H'; // Pick up results
      USB_Out_Buffer[NextUSBOut++] = 'C'; // Pick up results
      USB_Out_Buffer[NextUSBOut++] = 'M'; // Pick up results
      USB_Out_Buffer[NextUSBOut] = 0;
      RS232cp++; // 2017-09-04 Signal ready for next command
      mLED_3_Off();  // 2017-09-03 test code
      mLED_4_On(); // 2017-09-03 test code
    }
  }
  if(RS232_Out_Data_Rdy && !mAN8Busy() && RS232cp==2 ) // 2017-09-04 Do AN9
  {
    ADCON0 = 0x25; // 2017-09-04. Select AN5 (CHS=1001) and turn on ADC
    ADCON0bits.GO = 1; // Start conversion
    ++RS232cp;    // Indicate pending conversion
    mLED_3_On();  // 2017-09-03 test code
    mLED_4_On(); // 2017-09-03 test code
  }

  if(RS232_Out_Data_Rdy && RS232cp==3) // 2017-09-03 check if conversion done
  {
    if (!mAN8Busy())
    {
      USB_Out_Buffer[NextUSBOut++] = ADRESH; // Pick up results
      USB_Out_Buffer[NextUSBOut++] = ADRESL; // Pick up results
      USB_Out_Buffer[NextUSBOut++] = 'L'; // Pick up results
      USB_Out_Buffer[NextUSBOut++] = 'J'; // Pick up results
      USB_Out_Buffer[NextUSBOut++] = 'P'; // Pick up results
      USB_Out_Buffer[NextUSBOut] = 0;
      RS232_Out_Data_Rdy = 0; // Signal ready for next command
      mLED_3_Off();  // 2017-09-03 test code
      mLED_4_On(); // 2017-09-03 test code
    }
  }

  if((USBUSARTIsTxTrfReady()) && (NextUSBOut > 0))
  { // Send results to USB
    putUSBUSART(&USB_Out_Buffer[0], NextUSBOut);
    NextUSBOut = 0;
    mLED_3_Off();  // 2017-09-03 test code
    mLED_4_Off(); // 2017-09-03 test code
  }

  CDCTxService();
}               //end ProcessIO

Now you might notice that a lot of the code has to do with serial IO, which is not necessary for our analog to digital conversion. This will come in hand later as we refine our PIC IoT to work without the Raspberry Pi. Without the Pi as the USB host we will need to transmit our result using the serial port.

We then compile the modified sample code into a hex file which we then program using the Pickit 2:
./pk2cmd -PPIC18F14K50 -Fanalog_pic.hex  -M

If the stars are all aligned and everything goes perfectly (more likely after hours of painful but ultimately satisfying debugging) you get the correct blinkenlights on your development kit, and the PIC comes up as '/dev/ttyACM0'. We proceed to the next part, the application program to read the 'serial' port.

We whip out our python interpreter:

root@aspireF15:/home/heong/mpg$python
Python 2.7.11 (default, Mar  3 2016, 13:35:30)
[GCC 5.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import serial
>>> port=serial.Serial('/dev/ttyACM0', timeout=1)
>>> port.write('A'); port.read(10)
1
'\x00\x00HCM\x00\x00LJP'

OK, we have a valid reply but is reading zeroes in out analog input. Time to connect AN8 and AN9 to a reference voltage, 5V.

>>> port.write('A'); port.read(10)
1
'\x03\xedHCM\x00\x00LJP'

With multimeter, AN8 measures 4.94V and the 5V rail measures 5.01V. Assuming 3ff(1023) is 5.01, so 3ed is 1005, we have 4.92V (1005 / 1023 * 5) which is pretty close!

Next we look to measuring the solar battery voltage, which we expect to be around 12V. We will need a voltage divider.

Use 10K resistor for Z1 and 3K resistor for Z2

If we use 10K and 3K resistors, this will allow for a 22V maximum at the solar panel. You should use 1% tolerance wire-wound resistors. I happened to have only 20% carbon film resistors, so I used those and hope the error can be calibrated away. Note the carbon resistors should be much worse over temperature and age. 

After some hurried programming with the solder programming language, we connect the USB pic to the battery positive and negative terminals (watch it- reversing the connection may fry your USB device or your laptop!) and we now get:

>>> port.write('A'); port.read(10)
1
'\x03\x18HCM\x00\xdfLJP'

0318 hex is decimal 792. If full-scale is hex 3ff (decimal 1023). Estimating full-scale as 5V x 13/3, and calculating 792/1023 * 5 * 13 / 3 or 16.8V. With voltmeter I get 15.19. Which is not brilliant, but reasonable.

This has been my longest post to date. I hope it did not look too difficult - it really isn't. There may be many new things like microcontrollers (PICs), USB, and python, but the sample code from the Low Pin Count Development Kit really does work out of the box. I do not know everything mentioned here 100%. I know just enough to get the project going, and that is the norm in these manic days of ever-diminishing cycle time.

In the next post I will link up the analog input USB PIC18F14K50 to my website, and this makes it part of the Internet of Things.

Good luck and happy trails.

Saturday 9 September 2017

The Itinerant Solar Panel

Sometime back we visited Bernard Ng's orchard where he showed me his solar power rig, and I know I got to have one. I bought the same make of solar panel, SC Origin's 100W SPM100-M. I was quite pleased with it; so pleased that after two years in the guest room it was time it got mounted.

Solar panel facing west

I did not want a permanent mounting yet- the idea was to experiment with different locations and angles and collect data on the amount of charge I can collect from it. It needed to be stable enough not to get blown over, yet portable enough to be moved. At first I got a solar mounting frame:


But moving it was a little fraught. I found myself wrapping the panel in bubble wrap before moving it.

I finally settled on a very cheap (about RM60) plastic table, very common in Malaysia.


I simply unscrewed the table top from its metal base and mounted the solar panel in its place. The plastic table top disappeared under my 1969 Volkswagen Beetle as an oversized oil pan.

The solar panel needed to be mounted at an angle sometimes, and I nailed together a few bits of wood to hold it in place (otherwise the table tends to snap shut in the fully-folded position). To hold everything down, I made the wooden brace to fit the base of the battery. I used an NS60 sealed lead acid car battery.



Lastly the panel and battery needed to be connected to a solar charge controller, the 5A Gamma 2.0.
And mounted in place:

The Gamma came with an after-dark timer, so I connected a 35mA 12V LED night light to it.

Here is a charging graph, over some three days and two nights. The panel faced west so the morning charging starts slow, ramping up from 2pm until 6pm. The battery voltage here is seen to go well over the maximum 13.8V.

The discharging slope corresponds to the 35mA nightlight as well as a Raspberry Pi Model A (the original one) as well as a derivative of the Microchip Low Pin Count Development Board, probably 200mA or so in total.

By adding a WiFi USB dongle, the Wi Pi by Element14, we get a very rough-and-ready first version of an Internet of Things Solar Battery Voltage Monitor, which is the subject of the next post, so stay tuned!