adding 16 channel multiplexer to increase inputs

Hey All,

I have added a 16 channel multiplexor to increase the available analog inputs to the emonTX. All is going well so far in the affect of reading through all the channels on the multiplexor and reading multiple CT's being channeled into a single analog IO on an Arduino. My reading of the analog IO straight indicates that it is reading a value just fine, but when reading the calculated values the numbers drop with each sample and reading of the ct channel. same goes for the calculated voltage reading. Bear in mind I'm in the states and running 110v mains.

I also had to dig into the EmonLib.cpp and EmonLib.h files to integrate the code needed for the 16 ch multiplexor.

the LipoV and LipoSOC are for my lipo fuel guage IC, so never mind those. See below how the values decrease with each increment. 


LipoV=3971.25   LipoSOC=72.74   Main-1: 21918 Main-2: 18525 Volts: 179.48
LipoV=3971.25   LipoSOC=72.74   Main-1: 10701 Main-2: 7752 Volts: 126.11
LipoV=3972.50   LipoSOC=72.74   Main-1: 5597 Main-2: 3689 Volts: 91.20
LipoV=3972.50   LipoSOC=72.74   Main-1: 3160 Main-2: 1817 Volts: 68.61
LipoV=3972.50   LipoSOC=72.74   Main-1: 905 Main-2: 741 Volts: 36.69
LipoV=3972.50   LipoSOC=72.74   Main-1: 213 Main-2: 437 Volts: 17.92
LipoV=3973.75   LipoSOC=72.74   Main-1: 138 Main-2: 283 Volts: 14.52
LipoV=3973.75   LipoSOC=72.74   Main-1: 81 Main-2: 193 Volts: 11.16
I also wanted to ask for an explanation of this code in EmonLib.cpp
long EnergyMonitor::readVcc() {
  long result;
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  while (bit_is_set(ADCSRA,ADSC));
  result = ADCL;
  result |= ADCH<<8;
  result = 1126400L / result;
  return result;
Mostly this part
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
But in general what this function does?
Thanks in advance for any insight.
Great work guys, I love this project.
see for my take on your project.
Robert Wall's picture

Re: adding 16 channel multiplexer to increase inputs

"the values decrease with each increment"

Have you allowed the high pass filter to settle?  (That's the software filter:

filteredV = 0.996*(lastFilteredV+sampleV-lastSampleV);

that removes the effect of the Vs/2 offset applied in the hardware).

How are you de-multiplexing the values in the software?


"long EnergyMonitor::readVcc()"

Look at the Atmel data sheet (page 251)!  It is reading the supply voltage to the chip itself. If you look at the rest of the code, you can see how that voltage is used to adjust the calibration so that the final value is not dependent upon the voltage supplied to the chip.

jstoaks's picture

Re: adding 16 channel multiplexer to increase inputs

I'm using a CD74HC4067 16 channel IC. which is like a 16 position switch. I have added this function to EmonLib.cpp

​This function is from the Bldr tutorial for this IC.

 int readMux(int channel){

int s0 = 7;
int s1 = 8;
int s2 = 9;
int s3 = 10;
//Mux in "SIG" pin
int SIG_pin = 0;
  pinMode(s0, OUTPUT);
  pinMode(s1, OUTPUT);
  pinMode(s2, OUTPUT);
  pinMode(s3, OUTPUT); 
  digitalWrite(s0, LOW);
  digitalWrite(s1, LOW);
  digitalWrite(s2, LOW);
  digitalWrite(s3, LOW);
 int controlPin[] = {s0, s1, s2, s3};
  int muxChannel[16][4]={
    {0,0,0,0}, //channel 0
    {1,0,0,0}, //channel 1
    {0,1,0,0}, //channel 2
    {1,1,0,0}, //channel 3
    {0,0,1,0}, //channel 4
    {1,0,1,0}, //channel 5
    {0,1,1,0}, //channel 6
    {1,1,1,0}, //channel 7
    {0,0,0,1}, //channel 8
    {1,0,0,1}, //channel 9
    {0,1,0,1}, //channel 10
    {1,1,0,1}, //channel 11
    {0,0,1,1}, //channel 12
    {1,0,1,1}, //channel 13
    {0,1,1,1}, //channel 14
    {1,1,1,1}  //channel 15
  //loop through the 4 sig
  for(int i = 0; i < 4; i ++){
    digitalWrite(controlPin[i], muxChannel[channel][i]);
  //read the value at the SIG pin
  int sampleI = analogRead(SIG_pin);
  //return the value
  return sampleI;
I have changed the calling IF statements to the following:
void EnergyMonitor::currentTX(int _channel, double _ICAL)
   if (_channel == 1) muxCh = 0;  // formerly inPinI = 3;
   if (_channel == 2) muxCh = 1; //  formerly inPinI = 0;
   if (_channel == 3) muxCh = 2;  // formerly inPinI = 1
   if (_channel == 4) muxCh = 3; //  formerly inPinI = 4; // added for a 4th CT
   ICAL = _ICAL;


I have changed the analogRead(inPinI); to the following.
    // A) Read in raw voltage and current samples
    sampleV = analogRead(inPinV);                 //Read in raw voltage signal
    sampleI = readMux(muxCh);
This should switch the channels as called and funnel all the reading into Analog 0.
I believe it calls the Mux channel and then reads the sample and then call the next channel.
If i add some print statements I can see the analog values for each mux channel. they sit around 510.
No change to the software filter for the high pass, I would think that it has more time to settle with the added functions to read the analog values.
I got the ReadVCC() function, Thanks for the explanation.
Not sure if all this will really work, it almost feels like a timing issue with the multiplexor changing channels.
Open to any suggestions.
Best regards to all
Robert Wall's picture

Re: adding 16 channel multiplexer to increase inputs

Wait. It's not clear to me what you are actually doing in the software. I'm OK with controlling the multiplexer, it's what you do with the output of the ADC that is not clear. Are you reading the channels in sequence, or are you reading one channel for (say, as in the default software) 10 cycles of line frequency and calculating the rms, then stepping on to the next channel?

The way I would have done it is: In the sketch (.ino) create 16 instances of EnergyMonitor then in the loop( ) function call the Calc( ) method on all 16 in turn. Of course, you will need to integrate the multiplexer control with this as you appear to have done, but you'd only switch the multiplexer at the start of each 10 cycles worth of readings. Apologies if you are doing it that way, but as you switch the multiplexer before each reading it seems like you're not.

Check the time it takes to take a reading. If you are reading the 16 channels in sequence, then you may well be only sampling each channel less than 3 times per cycle, so uncomfortably close to the Nyquist frequency and you cannot expect a faithful representation of the waveform.

The time constant for the filter with only one channel is several seconds, if you are reading 16 channels then because the time constant is related to iterations round the loop, not real time by the clock, it will take 16 times as long - i.e. minutes. (The filter stops while you are working on other instances, then restarts as that input is sampled again).


jstoaks's picture

Re: adding 16 channel multiplexer to increase inputs

"or are you reading one channel for (say, as in the default software) 10 cycles of line frequency and calculating the rms, then stepping on to the next channel?"

Yes, as in the default software. But I am only actually reading the first four channels at the moment. So it doesn't take too much longer than the default 2 CT configuration using the ATMega328 ADC's.

My intention was to treat the multiplexer channels in the same fashion as the original EmonLib.cpp calls and sets the analog inputs. I did not mention earlier that The AC 9V supply is still on the default analog(2).

Instead of saying 


void EnergyMonitor::currentTX(int _channel, double _ICAL)
   if (_channel == 1) inPinI = 0;  // Analog ADC's on Atmel328
   if (_channel == 2) inPinI = 1;
   if (_channel == 3) inPinI = 3;
   if (_channel == 4) inPinI = 4;
   ICAL = _ICAL;
Change the Call of the Pin assignment to 
void EnergyMonitor::currentTX(int _channel, double _ICAL)
   if (_channel == 1) muxCh = 0;
   if (_channel == 2) muxCh = 1;
   if (_channel == 3) muxCh = 2;
   if (_channel == 4) muxCh = 3;
   ICAL = _ICAL;
So when ct1.CalcVI() is executed in the .ino loop the calcVI function is called upon, the currentTX channels are set to work with the multiplexor input assignments.
The calcVI function then sets sampleI = readMux(muxCh); The IF statements dictate which muxCh is active.
The readMux routine sets the channel specified in muxCh, readAnalog(0) is called to return the value to sampleI
I hope that all makes sense, I tried to make this function transparent to all the calculation functions.
If I add some print statements to the call of sampleI, I can see it sample about 50 times and then move to the next CT channel and by bread boarding  some leds in with the signal inputs for the multiplexor, I can see the multiplexor changing channel inputs corresponding with the CT channel.
Best regards,


Robert Wall's picture

Re: adding 16 channel multiplexer to increase inputs

The voltage input should make no difference to your problem. In the standard, it is sampled alternately with current, no matter which CT is being read. You will be doing the same.

It is starting to look like a timing problem. I'm not questioning how you are switching the multiplexer, you say it is working correctly - but I am questioning when. If you are still (re-)setting the multiplexer before each reading, then I would think the favourite explanation for the drift is the output from it has not settled before it is grabbed by the sample-and-hold within the ADC input circuitry. You only need to switch the multiplexer at the head of calcVI( ) - you can do it before it enters the loop to wait for the voltage zero crossing, so the multiplexer will have settled long before the first sample is read.

Do bear in mind that because the filter is a high-pass with a time constant of around 250 times the sampling period (i.e. about a second), and because it gets a half full scale step at startup, it takes many seconds to settle. What value does the input settle to after several minutes with a constant input?

jstoaks's picture

Re: adding 16 channel multiplexer to increase inputs


Thank you for all your insight, and logical thoughts. It would appear as though my issue may be hardware related. Just for giggles I removed the multiplexor and returned to the original EmonTX design and current firmware and continue to get the same results. I had it working before I added the multiplexor, so I will have to dig into the hardware for errors, I would love to continue this thread after I have resolved my issue at hand.

again Thanks for your support,

Best regards,


Robert Wall's picture

Re: adding 16 channel multiplexer to increase inputs

I hope you track down your problem. When you get it working, please post the full details in the spirit of open source so that others can benefit from your experience.

If you still have problems, then by all means revive this thread, but if I don't spot it you might need to PM me to wake me up! Unfortunately, this website software won't allow me to flag a single thread to watch.


Pierre's picture

Re: adding 16 channel multiplexer to increase inputs

Hi, I think I have the same king of problem.

I was trying to control a solid states relay with current load.

I loaded the emontx on my arduino and add a simple line like:

if current > set point;

high output 2

else low output 2


I test it with digital i/o 13 to see with the onboard led if it was working, and all was working just fine.

but when I use pin 2 or 4 and that the pin is switch to LOW then values start to be erratic, when It goes back to HIGH then all restart to work fine, and again as soon as the pin is switch to LOW nothing work correctly.


If you what, you could test just for fun to let pin 7, 8, 9 and 10 to high, to see if you have the same problem.


I test something, remove all my modify line and just add a line LOW 2 in setup and I have the same problem but when I mod it to HIGH 2 all is ok... weird

Robert Wall's picture

Re: adding 16 channel multiplexer to increase inputs

Are you sinking or sourcing current, and how much are you drawing from the output? If it works fine with an LED but not with the SSR, then I suspect you are not driving the SSR properly. Check the Atmel data sheet, and your SSR data sheet, for the voltages and currents required by each. Note the Arduino has significant differences between sinking and sourcing.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.