I developed an energy monitor for the PIC24EP256MC202 a few years ago. There was no library for a PIC, at least if there was no one told me. I doubt if there is a library now. So you would probably have to write your own.
This would not be a trivial task. You would have to understand the operation of your PIC in some detail, particularly the ADC component, then incorporate relevant code from emonLib.
What I did was to use the code for filtering and summing the ADC measurements using the articles in the Learn section on digital filters, and adapting the existing emonLib code that did the filtering and summing of the measurements. That was all I used from the openenergymonitor project. My monitor transmits data wirelessly to a PC, which saves the data in a SQL Server database for analysis.
There were a couple of main differences from EmonLib for the ADC retrieval and processing.
First the PIC24EP256MC202 provides simultaneous sampling of four analog pins rather than sequential sampling. So I didn’t do the phase correction in Explanation of the phase correction algorithm. From a quick look at the datasheet for the PIC16f877a the ADC module doesn’t provide simultaneous sampling, so you would have to do this phase correction.
The other major difference was that all my code was interrupt driven with nested interrupt levels. From memory the EmonLib code used the waveform shape to decide when to sample, or something like this. I didn’t need this code.
If you wanted to write ADC code for your PIC, you’d have to read chapter 11 of the PIC168F877A datasheet carefully. The operation of the ADC module is different from that in the PIC24EP256MC202. However to give a flavour of what you would have to do, I’ve included the section of my ISR code that handles an ADC interrupt, and also the calcVI module that does the filtering and summing (based on the module in the EmonLib code at that time).
This is the ADC interrupt routine. The four channels I measure simultaneously are AC voltage (using an Ideal Power Adaptor and Grid Current, PV generated current and generated current diverted to my immersion heater (using SCT013s).
/*
* ADC ISR
* This is called every second sample. The sample data to be processed is in
* one half of the ADC buffers while the other half is being filled.
* calcVI(V,Igrid,Ipv,Iim) is called for each of the two samples to update running
* totals.
* Every 5 seconds the totals are saved and a flag set so the user code processes
* the new measurement
*
*/
void __attribute__((interrupt, no_auto_psv)) _AD1Interrupt(void)
{
//// LogUS10();
if (!readyToMeasure) //Still waiting for hardware to stabilise?
{
ADCInterrupt=1; //Let the ADC initialisation code handle it
_AD1IF=0;
return;
}
MoveBuffers(); //Copy from latest 8 ADC buffers
//Update measurement counters
//Accumulate values from V,I,PVI IMI samples (channels 1,2,3, channel 0)
int i;
for (i = 0; i < 8; i += 4) //For each of the two sample fours
{
calcVI(ADCValues[i+1], ADCValues[i + 2],
ADCValues[i + 3], ADCValues[i]); //(V,I,PVI,IMI)
}
_IPL=T1_PRIORITY;
msNow=ms;
_IPL=ADC_PRIORITY;
//Return unless it's time to finalise this 5 second measurement
if (msNow - countStart < MEASURETIME)
{
// LogUS10();
_AD1IF=0;
return;
}
//Initiate new measurement processing
SaveTotals(); //Save the current measurement totals
ResetMeasurementTotals(); //Clear totals for next set of samples
newMeasurement=true; //User code will process the measurement
// LogUS10();
_AD1IF=0; //Allow next ADC interrupt
}
This is the calcVI routine that does the filtering and summing.
/*
* Based on OpenEnergyMonitor design. See their web site
* Called from ADC ISR to update totals for new sample. The ISR is interrupted
* every 90us for each pair of samples and calcVI is called for each sample.
* The normal 2 sample pair processing was measured as 30us. Each 5 secs totals
* are saved and reset taking 20us. So there is plenty of time spare out of the
* 90us between interrupts. Should probably recheck once variable gain has been
* added, but it's difficult to see there could be a problem.
*/
void calcVI(unsigned int NewV, unsigned int NewI,
unsigned int NewPVI, unsigned int NewIMI) {
//-----------------------------------------------------------------------------
//Apply digital high pass filters to remove 1.5V DC offset (centered on 0V).
//-----------------------------------------------------------------------------
numberOfSamples++;
filteredV=(long)NewV-VOffset;
VShiftedOffset+=filteredV;
VOffset=(int)((VShiftedOffset+FILTERROUNDING)>>FILTERSHIFT);
filteredI=(long)NewI-IOffset;
IShiftedOffset+=filteredI;
IOffset=(int)((IShiftedOffset+FILTERROUNDING)>>FILTERSHIFT);
filteredPVI=(long)NewPVI-PVIOffset;
PVIShiftedOffset+=filteredPVI;
PVIOffset=(int)((PVIShiftedOffset+FILTERROUNDING)>>FILTERSHIFT);
filteredIMI=(long)NewIMI-IMIOffset;
IMIShiftedOffset+=filteredIMI;
IMIOffset=(int)((IMIShiftedOffset+FILTERROUNDING)>>FILTERSHIFT);
sqV = filteredV * filteredV; //1) square voltage values
sumV += sqV; //2) sum
sqI = filteredI * filteredI; //1) square current values
sqPVI = filteredPVI * filteredPVI; //1) square current values
sqIMI = filteredIMI * filteredIMI; //1) square current values
sumI += sqI; //2) sum
sumPVI += sqPVI; //2) sum
sumIMI += sqIMI; //2) sum
instP = filteredV * filteredI; //Instantaneous Power
sumP += instP; //Sum
instPVP = filteredV * filteredPVI; //Instantaneous Power
sumPVP += instPVP; //Sum
instIMP = filteredV * filteredIMI; //Instantaneous Power
sumIMP += instIMP; //Sum
}
I’m not a PIC expert. As Robert says, you can try the Microchip forums for help
Good luck!