Spark & Spark Relay controlled Peristaltic Dosing Pumps

Thanks! Yes, my wife has requested something between the “Honey I Shrunk The Kids”, and “Peewee’s Big Adventure” style machine. I have old designs for a small scale one somewhere, but I think enough time has passed that a fresh design is in order. :slight_smile:

1 Like

When i saw this project i.wanted to make mobile.web page.

Anyway today i had free time to make this project still in process but work :))

when i finished i will share my project on this post.

Demo : http://yardimal.com/spark/

Sent from lg mobile.

2 Likes

Thats pretty amazing…Keep me posted!!! The ability to have both mobile and desktop is even better!!!

This forum continues to amaze me.

I need to pay it forward some how…I be thinking…

2 Likes

One little update, I received the VCC LED light pipes today from mouser. Works perfect!! Now I just need to find a reset switch and a couple of manual on/off switches for manually running the pumps.

3 Likes

I posted new firmware and HTML interface over in this thread:

Give it a try and let me know how it goes!

So I connected with someone who built a pretty sophisticated aquarium controller. The system is arduino based, but one of the features he has is a calibration setup for the dosing pumps. He mentioned I should try and get this ported over as the calibration works very well and helps a lot with the dosing. Also by doing this he has given the users the option to dose by time or by quanitity. I grabbed what I think is the code section for the dosing pumps and was curious if this could some how be incorporated into what you have written?

Here is a link to the thread: http://forum.reefangel.com/viewtopic.php?f=11&t=2328

A description of how the calibration works:

CalibrateDosingPumps() - I wrote this function as I’m about to
finally enable my dosers. You flip the memory bit to true and it will
turn on both pumps for 10 minutes. Then you can measure how much fluid
was pumped and set your pumps accordingly. It will get used once in a
blue moon, but hopefully someone will find it useful.

Here is the code:

// Definitions for dosing pumps
#define numDPumps 3 
byte pump[numDPumps]={ DPump1, DPump2, Extension}; // Pump 3 is just for logging/calibration routine
byte varReport[numDPumps]={ Var_DPump1, Var_DPump2, Var_WCVol};
byte memDPTime[numDPumps]={ Mem_B_DP1Timer, Mem_B_DP2Timer, Mem_B_DP3Timer};
int memDPVolume[numDPumps]={ Mem_I_DP1Volume, Mem_I_DP2Volume, Mem_I_DP3Volume };
int memDPRepeat[numDPumps]={ Mem_I_DP1RepeatInterval, Mem_I_DP2RepeatInterval, Mem_I_DP3RepeatInterval };
int memCalTime[numDPumps]={ Mem_I_CalDP1Time, Mem_I_CalDP2Time, Mem_I_CalDP3Time };
int memCalVol[numDPumps]={ Mem_I_CalDP1Vol, Mem_I_CalDP2Vol, Mem_I_CalDP3Vol };

void RunDosingPumps() {
  float rate;
  byte dpTime;
  int dpRepeat, calcTime, totalVolume;
  const int numPumps = numDPumps-1; // -1 = Remove "Extension" from the equation.
  
  static byte doseTime[numPumps]={ 
    InternalMemory.read(memDPTime[0]), 
    InternalMemory.read(memDPTime[1]) 
  };
  
  for (int i=0;i < numPumps; i++) {
    dpTime=InternalMemory.read(memDPTime[i]);
    dpRepeat=InternalMemory.read_int(memDPRepeat[i]);
    rate=(float)InternalMemory.read_int(memCalVol[i])/InternalMemory.read_int(memCalTime[i]);
    calcTime=(InternalMemory.read_int(memDPVolume[i])/rate)/(1440/dpRepeat);
    totalVolume=rate*(dpTime*(1440/dpRepeat)); 

    if (dpTime!=doseTime[i]) { // Memory has changed.
        InternalMemory.write_int(memDPVolume[i], totalVolume); // Update volume
        doseTime[i]=dpTime;
    } else if (dpTime!=calcTime) { // Calculated time has changed.
        InternalMemory.write(memDPTime[i], calcTime); // Update time
        doseTime[i]=calcTime;
    }

    // Make sure we're not calibrating
    if (!ReefAngel.Relay.Status(VO_Calibrate)) 
      ReefAngel.DosingPumpRepeat(pump[i], i*5, dpRepeat, doseTime[i]);
  }
}

void LogDosingPumps() {
  static time_t pumpTimer[numDPumps];
  static boolean pumpStatus[numDPumps];
  float rate;

  for (int i=0;i< numDPumps;i++) {
    if (ReefAngel.Relay.Status(pump[i])) {
      if (!pumpStatus[i]) {
        pumpTimer[i]=now()-pumpTimer[i]; // Pump was off, timer is now a time
        pumpStatus[i]=true;
      }
    } else {
      if (pumpStatus[i]) {
        pumpTimer[i]=now()-pumpTimer[i]; // Pump was on, timer is now a timer
        pumpStatus[i]=false;
    
        rate=(float)InternalMemory.read_int(memCalVol[i])/InternalMemory.read_int(memCalTime[i]);
        ReefAngel.CustomVar[varReport[i]]=pumpTimer[i]*rate;
      }
    }

    if (now()%SECS_PER_DAY==SECS_PER_DAY-1 && i!=2) { // Don't overwrite WC log
      pumpTimer[i]=0; // Clear timer at end of day
      ReefAngel.CustomVar[varReport[i]]=0; // Clear portal variable
    }
  }  
  
  // Reset logging for WC fill pump at start of WC
  static boolean wcStart;
  if (ReefAngel.DisplayedMenu!=WATERCHANGE_MODE) wcStart=false;
  if (ReefAngel.DisplayedMenu==WATERCHANGE_MODE && !wcStart) {
    ReefAngel.CustomVar[varReport[2]]=0;
    pumpTimer[2]=0;
    wcStart=true;
  }
}

void CalibrateDPumps() {
  static time_t pumpTimer[numDPumps];
  static boolean pumpStatus[numDPumps];
  static boolean running=false;
      
  if (ReefAngel.Relay.Status(VO_Calibrate)) { 
    running=true;
    
    for (int i=0;i < numDPumps;i++) {
      if (ReefAngel.Relay.Status(pump[i])) {
        if (!pumpStatus[i]) {
          pumpTimer[i]=now()-pumpTimer[i]; // Pump was off, timer is now a time
          pumpStatus[i]=true;
        }
      } else {
        if (pumpStatus[i]) {
          pumpTimer[i]=now()-pumpTimer[i]; // Pump was on, timer is now a timer
          pumpStatus[i]=false;
        }
      }
    }      
  } else {
    if (running) {
      running=false;
      
      for (int i=0;i < numDPumps;i++) {
        if (pumpTimer[i]>0 && !ReefAngel.Relay.Status(VO_LockPorts)) {
          InternalMemory.write_int(memCalTime[i], pumpTimer[i]); 
        }
        ReefAngel.Relay.Override(pump[i],2); // Go back to auto mode 
        pumpStatus[i]=false;
        pumpTimer[i]=0;
      }
    }
  }    
}

void AutoWaterChange() {
  int runtime=InternalMemory.read_int(Mem_I_WCFillTime); 
  static WiFiAlert wcAlert;
  static boolean started;
  static time_t t;

  if (ReefAngel.DisplayedMenu==WATERCHANGE_MODE) {
    ReefAngel.Relay.On(Refugium);

    if (ReefAngel.Relay.Status(VO_EnableATO)) {
      
      ReefAngel.SingleATOHigh(Extension); // Refill fresh SW as needed

      if (InternalMemory.read(Mem_B_MaintWC) > 0) // Reset last WC counter 
        InternalMemory.write(Mem_B_MaintWC,0);
      
      if (ReefAngel.Relay.Status(VO_StartFill) && !started) {
        ReefAngel.Relay.Override(Reactor,1); // Start draining
        started=true;
        t=now();
      }
      
      if ( (now()-t > runtime && started) || bitRead(ReefAngel.AlertFlags,ATOTimeOutFlag) ) {
        ReefAngel.Relay.Override(Reactor,0); // Stop draining
        ReefAngel.Relay.Override(VO_StartFill,2); // Reset Start switch
        if (started) wcAlert.Send("Reactor+disabled.", true);
        started=false;
      }
      
    } else {
      // Done with ATO
      ReefAngel.Relay.Off(Extension); 
      // Backup WC Vol
      if (InternalMemory.read(Mem_B_MaintWCVol) != ReefAngel.CustomVar[Var_WCVol])
        InternalMemory.write(Mem_B_MaintWCVol, ReefAngel.CustomVar[Var_WCVol]);
      // Clear stale timeout
      ReefAngel.ATOClear();
    }
  } else {
    ReefAngel.Relay.Override(VO_EnableATO,2);
    ReefAngel.Relay.Override(VO_StartFill,2);    
  }
}
1 Like

So I polled a bunch on people on my of aquarium threads and there is a lot of interest in this project. One of the options I want to offer people is a much less expensive option, which means using 12Vdc pumps. The problem I ran into was the lack of simple options for integrating a PWM motor shield. With that in mind, I came up with some options so the speed of the doser pumps could be altered via a manual PWM controller. I haven’t decided whether it will be a single PWM controller (less accurate) or 2-3 (depending if there are 2 or 3 dosers) PWM controllers to accurately adjust each doser.

Right now I’m looking at two pots options, one is a bourns multi-turn pot with a bourns turn-counting dial with lock. The other option is a bourns trimmer pot. The panel mount option for the trimmer pots is expensive, so it would need be adjusted by taking the lid off. Not ideal, but its a price to pay for an economical doser.

My previous post made note of a calibration option, not sure if anyone has had any time to look it over or not, but I don’t think it will matter much if we end up going with a manual PWM option.

Does anyone know if there are any thoughts of making a true Spark motor shield? Personally I don’t need stepper capabilities, but PWM would certainly be nice.

I’m open for feedback!

And once again, I can’t thank @bko enough for writing the firmware.

1 Like

Is this helpful? :slight_smile:

Ha! I’ve seen that before…in fact you may have introduced me to it. Those are great, but a bit on the pricey side.

Not shutting you down, but most of the doser’s that are available to reef hobbyists start at over $250. The goal is to make this a DIY project for people so they don’t have to take out a mortgage to buy one. Its an expensive hobby to say the least.

1 Like

This!

Some available products to consider but we can always design a motor shield if needed :slight_smile:

Thanks for that. I actually purchased one of those, and then one that looked identical on ebay for $10, in the end I’m pretty sure they are exactly the same. At that point I purchased one more from adafruit and one more from ebay as I wanted to compare the pumping rates. All four have slightly different pump rates which was disappointing. Now I didn’t connect them to any type of controller, I just plugged them into a breadboard with a 12v power supply. Could that have anything to do with it?

I’m really doing a lot of research and spending some cash on this project. Mostly because I love the electronics, and I love my reef aquarium. I don’t plan on building these and selling them, I plan on putting an extensive write up together so people can do it themselves. But in the end I want the product they build to work well and work according what I share.

I don’t have loads of cash laying around, but I can tell you right now I would buy a few motor shields and I could probably convince people to buy them if they were available. Of course then I would have to bug @bko to help me with the code - not sure if he’d be happy about that or not…haha.

3 Likes

I was reading your post and started thinking of really cheap PWM drivers with position feedback too… Radio control servos are just that, and totally hackable!

you could 3d print the pump bit to fit on a servo.

i cant remember what brand it was… but there is one you can bypass the feedback controller chip and feed PWM straight into the driver, and then take the signal from the feedback pot into your controller.

I’ll be honest @Hootie81, you kind of lost me on this one. I understand how servo motors work but I’ve never know them to have enough torq to pump liquids. I’m very open to ideas and learning but I’m also a visual person for grasping the entire ides.

I appreciate your out of the box thinking and if it’s something you can show an example of I’d very open to trying it.

Thank you

What sort of volume are you pumping? And what rate do you need?

I’m thinking something small… maybe using silicone tube used for RC fuel lines so it doesn’t take much force to compress

I have a 3d printer so I’ll draw one up and give it a try. I’ve got boxes of servos and I’m sure even a standard one will have enough torque.

With my case specifically, it could be around 16mL/per (215mL per day). A larger aquarium could need a lot more or a smaller tank could need as little as 1mL/hr.

Cool, i’ve started to draw something up… hopefully i can give it a try over the next day or 2. i think those rates would be achievable

now that the weather has dried up a bit i need to fix my roof thats falling in! but ill continue to draw it up tonight

Ha! Your home is much more important, not rush.

Nice and tidy work there.

I am curious though to how good a signal you get with the WiFi seeing as you have the thing inside a metal housing?

What sort of range are you getting from this?

I guess the version with the UFL and an external antenna would be good for longer range if needed.

I live in an apartment block with mainly solid reinforced walls and it’s a bit like living in a Faraday cage as I get hardly any WiFi signal 2 bedrooms away. I have had to install a second WiFi unit in the other room and use a powerline Ethernet module to connect this to the main router.

I actually have both cores, the uFL and regular. I haven’t noticed a difference between the two, but I guess it would be worth moving it around my apartment to see what happens.

So part of the research that I've been doing for this project has been on another forum. I got some feedback from someone which I do feel is important feedback, so I asked some questions in response - and of course after about five days I still have yet to receive a response. Could someone possibly chime in on this for me? I've done a bit of reading and believe I have found some answers, but I want to confirm that, as well as get some additional parts info.

Here is the comment from the member of the other forum:

If you want to go open loop then I suggest you keep the motor driver
impedances very low, as this reduces the effect of load slowing the
motor down. So it means more stable speed for any fixed PWM duty.

So you would need a high current regulated voltage PSU, and low
resistance PWM FETs and low resistance cabling. And large schottky
freewheel diodes at the motor terminals.

My Questions / Response

I'm starting with one of these as my PWM controller. I'm expecting criticism about this, and I'm prepared - so can you offer me a better option for a PWM controller if need be? Would this be better?

When you say high current, how high of current? I was going to start with one of these for testing.

Low Resistance PWM FETs

Can you point me to one on mouser, jameco, etc please? Would I want to replace the FET on the PWM controller with this?

And large schottky freewheel diodes at the motor terminals.

Can you point me to one on mouser, jameco, etc as well please?

I've attached a photo of an arduino motor diagram, is that the correct
way to hook up a diode? Can the diode be on the controller end rather
than on the motor since the motors will never be running at the same
time?

I'm sorry for asking for such details, but I really am a visual person,
and seeing schematics, drawings, or actual pictures really do help me
out.