dump load controller

Folks this is my first post. I hope I have not broken any of your guidance rules etc - apologies if I have.

I am trying to use an Arduino Uno purchased a few months ago to control a very small (“pico” ie <2kW) hydro generator and turbine supplying a village in Uganda. The scheme uses two induction motors as generators wired with capacitors in a “C-2C” arrangement to give very approximately 200V at very approximately 60Hz. The Arduino controls the system by monitoring grid voltage and switching in or out, via solid state relays, so called “dump loads” (extra resistance in the form of cooking elements) to keep the load on the generators and turbine roughly constant whatever the “consumers”/ villagers, switch on or off.

My problem is that the Arduino seems not to do what I expect. (I am a virgin as far and Arduino and C++ etc are concerned, but I did write a complicated BBC BASIC code ~about 3500 lines - many years ago for my PhD.) So far I have hit four or five problems which have cost me a lot of time:

I thought that I would measure “grid” voltage and frequency by feeding an Arduino analogue port with a full wave rectified but unsmoothed voltage from a step-down transformer and a resistor ladder. I put a 100nF across it to take out the radio-frequencies but there is no 1000uF etc to produce a conventional smooth rectified ac. (The arduino itself is fed from the same transformers via a second full wave bridge, but this time with a large capacitor.) The Arduino goes fast enough, that in one half cycle, it will measure voltage at least 60 times. I thought that it could take these measures and square them and sum them and average to produce a mean-square voltage. The square-root of this would be the root-mean-square of the grid voltage. (Much of the load on the network will be compact fluorescent lightbulbs with odd waveforms so I thought it safer to try to get a true RMS voltage.) And by using the interval “micros()” between corresponding points in adjacent half cycles, the half period of the ac voltage and therefore frequency would be obtained. But some difficulties came my way:

  1. My first approach was to run two while() loops to synchronise with the mains, then simply let the ADC run, taking the readings, squaring them, summing, then averaging, taking the square root and finally multiplying by a fiddle factor to get regular RMS volts. But I got rubbish. Ok second approach: get the thing to write ADC readings into an array and operate on those - this yielded more sensible results - but there was no real success until ADC 10bit output was right shifted twice ie made an 8_bit before squaring. In one of the umpteen versions of this I created another array to hold the Arduino square of the 10bit ADC readings and it was clear that up to 255, squares worked reliably, but above would usually give 0, but not always, and sometimes it was correct.

  2. Integer arithmetic does not seem to work. In the code attached I thought I would divide 500,000 by the half period of the waveform to obtain a frequency. I did not need great precision so I thought integer arithmetic would be good enough. But 500k/20k = 2 (sic) with Arduino rather than the 25 or whatever one might expect. Only if the expected output is a float (obviously) AND one of the inputs (nominator or denominator) is also a float, will a sensible answer be generated at least in the code attached.

  3. if() and else: the loop function has a series of if() statements. At the end originally I had an else, but the Aduino would execute a true if() AND then it would execute the else as well (sic).

  4. constrain(a,b,c) does not seem to work in the loop function: I cannot get a simple test program to fail nor the real function to work!

  5. I will happily concede that I am not top drawer material and that I do not understand the declaration of variables etc in C - particularly passed and returned variables. I used to do this successfully in BBC Basic, but I have had error messages in ARDUINO which defeat me - even when I duplicate (I think perfectly) working code.

  6. Philosophically why is there a setup() function AND the ability to declare and set things before it? I would have thought one could have either all the declarations etc in the setup() or setup() could be superfluous?

So my question is: is this what happens when you let a well-meaning but ignorant amateur loose - are these my errors or are there bugs in the Arduino OS? And what do I do to get code which works?

Any help gratefully received

Colin Oram, Fort Portal, Uganda.

dumpload_controllerV5er12dump_prop_2_err.ino (6.43 KB)

The source code is almost impossible to read for me. Could you please use one statement per line. There is also a auto-format option in the menu (it is in version 1.5.2). Could you add comment lines and more blank lines. I would like to see comments for a block of code, and explanation for the calculations. The variable names could be longer, it is hard to understand the "vp", "lev", "agg", "edi" names. The number of code lines will increase about ten times, but that is good.

About 900 bytes are declared on the stack in getVolts(). That is something that could cause a lot of troubles.

Do you know this website ? http://openenergymonitor.org/emon/

To echo Erdin, that is really, really hard to read. Here are a couple of things I did notice though:

This is a problem, as you observe:

freq=500000/(edus-stus);

Counterintuitively, the compiler treats constants as int, which are only 16 bits wide. 500000 is far too big to fit in an int - use 500000L to let the compiler know.

Constrain doesn’t change its arguments, it returns the constrained result. What you need is

dumpSt=constrain (dumpSt,0,7); does work!

Setup exists to do one time initialization, but you can’t declare variables there that you want to use elsewhere. Such variables need to be global and you need to declare them outside any function.

Edit: While the compiler undoubtedly has bugs, as a novice, it’s safe to assume that any issues, weird behaviour or defects are caused by your code or wiring. Suspecting the compiler is just a distraction, preventing you from finding the real problem.

Folks,

Thanks for at least two nuggets already. I have tried the “teddybear” technique but it only works if you actually know what is correct but have not noticed the error.

Erdin asked that I reformat the code and comment it so I have done so.

I am borderline dyslexic and find the conventional way of setting out C hard to read! Similarly with variables - I tend to make pointers very short.

He further asked if I had seen the website: http://openenergymonitor.org/emon/

I had not but will read with interest -thanks.

Wildbill set me straight with two errors - thanks so much.

But what is this stack business - I looked cursorily on Google, but do not really know what to look for. I understand the concept of a stack, but did not realise I had choices and constraints here.

I did rather think that these problems would be from my ignorance and that there would be explanations for them, but it is difficult to find this stuff by reading - or maybe my Googling skills are poor.

But the 500,000L fix seems a bit odd. The compiler can see the number so why does it still use the wrong precision (number of bytes)?

BW and many thanks

Colin.

dumpload_controllerV5er12dump_prop_2_err_commented.ino (9.27 KB)

But what is this stack business - I looked cursorily on Google, but do not really know what to look for. I understand the concept of a stack, but did not realise I had choices and constraints here.

It's not so much the stack per se, it's the small amount of RAM the uno has - only 2K. As noted above you're consuming 900 bytes of it when your getVolts function is executing. You also have a bunch of strings in your serial.prints, which also consume precious memory. If you use too much, your heap and stack will collide, with unpredictable results.

You can sort out the serial print using the F macro, as long as you're using the 1.0 version of the IDE or later:

    Serial.print(F("dumpState="));

You can also reduce your usage by using unsigned int to store your times instead of unsigned long. You will need to note the time when you start populating and just store the difference between millis and the start time in your array.

Philosophically why is there a setup() function AND the ability to declare and set things before it? I would have thought one could have either all the declarations etc in the setup() or setup() could be superfluous?

Pragmatically, the answer is scope. Variables declared inside a function, like setup(), are not accessible in any other function.

A very interesting project ...

My few cents worth ...

(1) Have you downloaded the Atmel datasheet doc8161.pdf - it gives all the gory details. I have been having trouble with jittery ADC readings from a 47k potentiometer (just trying to detect its position). From reading the datasheet it seems that it would do better with a lower impedance device - I think it says 10k in the datasheet. (Bear in mind that I consider myself vaguely competent with digital electronics but rubbish with analogue stuff.) It's also possible the potentiometer is bad - it''s old but the only one I've got at the moment. Long story short - check your impedances match properly.

(2) I suspect there is no need to calculate square roots - why not do all the work with the squared values and save a considerable amount of complex calculation. Indeed if you can physically separate out positive and negative cycles I don't think you even need to square the values.

(3) Ditto for frequency. You can probably use counts/per second as a proxy for frequency.

(4) If you want the Arduino to upload data to a PC for human interest you could do the complex calculations there.

...R

Are you really using pin0 for the voltpin? Doesn't that conflict with serial?

TanHadron:
Are you really using pin0 for the voltpin? Doesn’t that conflict with serial?

It’s used in analogRead, so it’s A0, not digital pin 0. No issue.

I was thinking more about this (fatal disease) ...

I think you could considerably simplify the application if you used one of the timers to trigger a read at regular intervals. That way you could average the readings without bothering with time measurements (as time would be constant).

I'm guessing if you have a bridge rectifier that the voltage perceived by the ADC is a series of positive half-sine waves. If you check whether ADC value is a minimum (or maximum) you could then read the time to identify frequency.

I read somewhere that the ADC performs better if the digital inputs of the Analogue pins are disabled by setting bits 5:0 of the DIDR0 register.

...R

Thanks for reformatting the source code.

You have calculations that mix float, integers and bytes. I looked at them where it could go wrong, but I still don’t know.

For example the function “switchDumps(byte dumpSt)”. The parameter is a byte, and you call the function with a float number.
You aso have two variables “dumpSt”, one is a float, and the other is a byte.
I think it will work, but if I write code I try to avoid this.
If I compare a float variable with a number, I use a floating point number, just to show what I’m doing.
Like this:

// old line: if(dumpSt>7){
if(dumpSt>7.0){

In the same way I use ‘L’ for long and ‘UL’ for unsigned long numbers. Just to remind myself what I’m doing.
For example, how would you want to do this calculation:

freq=500000.0/(edus-stus); 
freq=500000UL/(edus-stus);

You can do the division with unsigned long or with floating point.
The result will be either truncated to whole numbers or a floating point number.

I’m also not sure what the while loops are doing. The empty statement “;” is in front of the “{ }”.
Could you change that ?

  while (analogRead(voltpin)<thr)
  {
    ;    // wait until ADC level > timing trigger threshold
  } 
  while (analogRead(voltpin)>thr)
  {
    ;   // wait until ADC level < timing trigger threshold
  }

I tested your code on a Arduino Uno, and after declaring those large arrays on the stack, you still have 850 byte of ram. So you are okay for now.

Did you know that optimization by using bytes could increase the compiled code size?
Because the compiler has to do so many conversions before a byte can be compared with an integer or float.
Using only integers or only float could shorten the compiled code.
If you replace the bytes with integers or unsigned integers, that would already be an improvement.

I tried to look look into the code, how it works and what it is doing. But I can’t understand fully what could go wrong.
Is there a way to test every function seperately ?
You already show a lot of variables, perhaps you could add a few Serial.println() in the code, or when calling a fuction.
How are the analog input values ? You print the us values to the serial port. Do they have noise, causing false detection of zero crossing ?

Folks,

Thanks again for all your suggestions. A slightly different version of this code is now working in a small village called Mabwe on the eastern slopes of the Rwenzori mountains. But it is not stable and disconnects and reconnects power without obvious reason. So there is more work here.

Reformatting the code: the least I could do! Thanks again to so many of you for help.

I think the constrain problem was just silliness on my part - thanks for your patience.

I had assumed that the smaller the number of bytes in a variable the lower the processing load, but Erdin tells otherwise. It is for this reason that I have mixed variable types in the code - I used the smallest which would do the job. I had assumed that the system would convert a float into a byte and it seems to. I used a float for dumpSt in one part of the code to allow increments in its size to slow the response of the controller. But there are only three dump switches and therefore eight states possible so a fraction of a dumpSt makes no sense other than to provide a slowing action.

I still cannot get sense on integer division however. I have tried eg 500000L/(edus-stus) but still get silly answers. I declared a long unsigned variable called n500000 and set it to 500,000 but with no improvement. Only if the output variable is a float and ONE or BOTH of the nominator and denominator are floats, does it give sense. If both nominator and denominator are long unsigned, then we get an integer answer eg float freqf; long unsigned nominLU=500000; long unsigned denomLU=10110; freqf=nominLU/denomLU

gives 49.000

But the squaring of unsigned int ie two byte variables remains as does the if/else problem.

To answer Erdin's question on the empty while() statements, they are simply there to delay things until the waveform the ADC is measuring gets to a standard place and then allow the ADC to fill the array. Like this a shorter array is possible than if it starts at a random time. I wanted the routine to be able to cope with low frequencies eg 30Hz. I could take Robin2's suggestion and slow the ADC by timing it, but I would still have an unknown length of sample and therefore array length need. With an array with 200 slots we get error messages.

But in fact this voltage measuring routine seems robust and reliable as it is. I did develop it as per one of the suggestions separately and then cut and paste.

Actually my main problem is logic and unexpected or impossible results like all the LEDs going out. I cannot see how this is possible, but it happens a lot. I would like to write more sophisticated routines but my inability to get even simple stuff working dissuades me. A particular problem concerns making the grid connection. Induction generators cannot be allowed any load at start or stop else they lose magnetisation. It is therefore necessary to connect the load when they are running. But what dump load setting to go in at? The Arduino cannot know the demand and therefore what the dumps should be set at. So we go in at average or zero. There is then a period when the voltage is outside limits and the Arduino is of course instructed to protect the load and disconnect. This cycle goes on indefinitely in some conditions. I would like to write some learning ability so the Arduino notes why it disconnected and tries next time at a different dump setting or even to delay disconnection if it has just connected. But as above, I get weird results. The various time delays etc make it impossible to try ideas out without access to the actual system.

I must apologise for at this point for I must park the problem for a few days as I have to return to UK. I hope I can start it again in a few days. Many thanks again to you all.

Bolin.

void switchDumps(byte dumpSt) {
  switch (dumpSt) {
  case 0:
    digitalWrite(dump1p, LOW);
    digitalWrite(dump2p, LOW);
    digitalWrite(dump3p, LOW);
    break;
  case 1:
    digitalWrite(dump1p, HIGH);
    digitalWrite(dump2p, LOW);
    digitalWrite(dump3p, LOW);
    break;
  case 2:
    digitalWrite(dump1p, LOW);
    digitalWrite(dump2p, HIGH);
    digitalWrite(dump3p, LOW);
    break;
  case 3:
    digitalWrite(dump1p, HIGH);
    digitalWrite(dump2p, HIGH);
    digitalWrite(dump3p, LOW);
    break;
  case 4:
    digitalWrite(dump1p, LOW);
    digitalWrite(dump2p, LOW);
    digitalWrite(dump3p, HIGH);
    break;
  case 5:
    digitalWrite(dump1p, HIGH);
    digitalWrite(dump2p, LOW);
    digitalWrite(dump3p, HIGH);
    break;
  case 6:
    digitalWrite(dump1p, LOW);
    digitalWrite(dump2p, HIGH);
    digitalWrite(dump3p, HIGH);
    break;
  case 7:
    digitalWrite(dump1p, HIGH);
    digitalWrite(dump2p, HIGH);
    digitalWrite(dump3p, HIGH);
    break;  
  }//end switch(dumpState)
}//end switchdumps

Much simpler

void switchDumps(byte dumpSt) 
{
    digitalWrite(dump1p, (dumpSt & 1) ? HIGH : LOW);
    digitalWrite(dump2p, (dumpSt & 2) ? HIGH : LOW);
    digitalWrite(dump3p, (dumpSt & 4) ? HIGH : LOW);
}

or even:

void switchDumps(byte dumpSt) 
{
    digitalWrite(dump1p, dumpSt & 1);
    digitalWrite(dump2p, dumpSt & 2);
    digitalWrite(dump3p, dumpSt & 4);
}

Similarly:

void switchLEDs(byte LED) 
{
    digitalWrite(gdLEDp, LED == GOODLED);
    digitalWrite(loLEDp, LED == LOLED);
    digitalWrite(hiLEDp, LED == HILED);
}

Dear AWOL,

Thanks so much for your elegant solution - very neat. I thought there would be such methods, but with my experience so far I thought I would stick with what works, but I will adopt your solution now and save some programme space.

Thanks again.

Bolin.

I still cannot get sense on integer division however.

Post your code - I suspect you're missing a UL suffix on a constant somewhere.

Issues with squaring numbers > 255 sounds like an integer overflow issue too.

Talking of squaring things, I think I see an issue in your RMS logic. Everything you read from the ADC will be a positive number, so when the waveform is negative, you'll still be getting a small, but positive number. That's going to skew your results. It looks like you need to figure out what the ADC midpoint is and subtract it from each reading before you use it.

See point [2] in Post #6 above. I don't think there is any need to square anything.

...R

wildbill: Talking of squaring things, I think I see an issue in your RMS logic.