Basic relay use for irrigation solenoids

Hi all, This will be my first actual arduino project (beyond messing around with LEDs with my kids!) I want to install an irrigation system for my garden. The plan is to have 4 zones with standard orbit solenoid valves which are controlled to open and close sequentially with the arduino. I'm excited to play with the coding piece, and the valves and wiring and piping are all installed already. My question has to do with how to connect the arduino to the solenoid valves. I have a 24vac 750mA power supply for the valves, and I know that I'll need to use relays (like these), but I don't think that I can power the relay coils directly from the arduino pins right? Too much energy I think? I think that I need some kind of transistors to power the coils perhaps? And then what about a flyback diode to protect the circuit? Do I need that too? Also, will that relay work with 24vac? I don't see why not, but probably best to check with you all! Thanks so much for any help you can give me :)

About $4-50 on eBay will get you a board with four relays specifically made to be driven directly by Arduino. I believe it uses opto couplers to drive the coils.

It looks like you already know 95% of what is needed.

Study these:

|500x367

|500x449

Similar project is attached. You will find several versions of a sketch that you can modify to your needs. Read through this:

https://forum.arduino.cc/index.php?topic=394443.0

Code attached on post #177 .

Hi, Two issues:

I think you should use the optoisolated relay boards mentioned, like THIS:

See info on controlling power with Arduino in the ArduinoInfo.Info WIKI HERE:

I run 3 irrigation valves from these.

5V Power.. The relay COILS run from 5V. Each relay draws about .08A (80ma) from 5V Vcc so about 4 relays are the maximum you should run from the Arduino +5V supply. BUT you have the option (I use) of running only one irrigation zone at a time, so only one relay is on at a time. I also want to distribute the water needed over 5 hours in the middle of the night as I run from a well.

If you use a small Real Time Clock (RTC) module like THIS:then the time will be correct.

You will need to write code, but that should be the fun part :-)

DISCLAIMER: Mentioned stuff from my own shop...

Hey all, Thanks for the suggestions, but I really don't want to use off the shelf pre-assembled components. Part of the fun for me is in the building and learning new electronics skills. I'm a pretty hands on guy, but haven't done much with electronic circuitry on this scale (I can wire up a house with no problems!) I'm excited to use pcb boards and practice small scale soldering (again, I can solder copper pipes no prob...) I am equally interesting in figuring out the why piece of things. Like why use an optocoupler vs a transistor in this application, and why use caertain resistors or diodes, and how do you know which ones to use!

So a few more specific questions:

  1. Larry D - I am confused by the first diagram...a lot going on here...perhaps too much for me to understand at this point! Is there decent free software for making these kinds of diagrams? If so, maybe I can try to diagram out what I'm thinking about for a first attempt and then get feedback on that :)

  2. I understand why to use resistors to pull up/down when using switches (to avoid floating), but why would you use pull-up vs pull-down? It seems like with pull-up you're always wasting a little electricity, and with pull-down you're not. So pull-down seems like it makes more sense to me, yet pull-up seems more common (I think?) Why would you ever use pull-up?

  3. How do you figure out things like the amperage draw of relay coils? I can't seem to find this number in literature - do you just have to actually measure it? How do you know you're buying the right one then?

  4. For the switch part of the relay (not the coil), how are the ratings determined? For example the relay I linked to above is rated for: 7A 240VAC 10A 125VAC/28VDC. So at 240vac it can handle 1680 watts, at 125vac it can handle 1250 watts, and at 28vdc it can only handle 280 watts right? Why such a huge discrepancy? What is the limiting factor in these things?

  5. I was originally thinking about using transistors, not opotisolators to run the relay coil (I didn't know that optoisolators existed). Why would you use one vs the other? It seems like all the pre-fab boards use optoisolators...why? Are they more reliable? Cheaper? What's the advantage if any?

Thanks!!!

Thanks so much everyone!

  1. Larry D - I am confused by the first diagram...a lot going on here...perhaps too much for me to understand at this point! Is there decent free software for making these kinds of diagrams? If so, maybe I can try to diagram out what I'm thinking about for a first attempt and then get feedback on that :)

In the first image there are different ways of controlling LEDs, relays, and motors demonstrated. Concentrate on one example output at a time. Pick the method that best suits your application. There is a free version of Eagle you can use for schematics. I use WinQCAD (not available from the author any more) some free versions on the web still.

  1. I understand why to use resistors to pull up/down when using switches (to avoid floating), but why would you use pull-up vs pull-down? It seems like with pull-up you're always wasting a little electricity, and with pull-down you're not. So pull-down seems like it makes more sense to me, yet pull-up seems more common (I think?) Why would you ever use pull-up?

The input impedance of a pin is 100meg so very little current is consumed. There are two types of switches, N.O. and N.C. Your choice and your code determines if a pull-up or down is used.

  1. How do you figure out things like the amperage draw of relay coils? I can't seem to find this number in literature - do you just have to actually measure it? How do you know you're buying the right one then?

Resistance of a coil can be measured with a DVM or you can look it up on the data sheet. You buy a relay based on the current the coil draws, the coil voltage, the contact form, and the contact ratings.

  1. For the switch part of the relay (not the coil), how are the ratings determined? For example the relay I linked to above is rated for: 7A 240VAC 10A 125VAC/28VDC. So at 240vac it can handle 1680 watts, at 125vac it can handle 1250 watts, and at 28vdc it can only handle 280 watts right? Why such a huge discrepancy? What is the limiting factor in these things?

Contact size and composition relates to their rating. If you are driving: inductive, resistive, AC/DC loads will effect the maximum current capabilities.

  1. I was originally thinking about using transistors, not opotisolators to run the relay coil (I didn't know that optoisolators existed). Why would you use one vs the other? It seems like all the pre-fab boards use optoisolators...why? Are they more reliable? Cheaper? What's the advantage if any?

Opto isolators usually drive a transistor that drives the relay coil. There may be a requirement to have 100% isolation between the controller to the coil for noise considerations.

The relay itself can provide isolation from the coil to the contact load.

.

Larry D - Ok, I understand the diagram better now, I thought this was all meant to do something cohesive :)

Can you or anyone else :) clarify a few items on the diagram?

  1. What are the functions of R14, R15, R20? Why was a 1k resistor chosen for r14/r15, but a 470 ohm for R20? Why these resistances at all vs a lesser or greater resistance? What would happen if they were entirely excluded from the circuit?

  2. Is R18 a pull down resistor? Why this denomination of resistance?

  3. Does R19 serve the same function as R14/R15/R20? Is this necessary with a FET? In researching transistors, my understanding was that there is almost no current draw from the gate pin of a FET anyway.

  4. What is the function of R21? Wouldn't this arrangement result in a small current flowing into the base of Q9 and thus Q9 would always have flow between the emitter and collector? I don't understand what the arduino pin 6 does in this arrangement, it just seems like if LOW the transistor has a small flow through it (+5v, through R21, into the base), and if HIGH it has even more flow through it (+5 from pin 6, through R20, into the base)... either way the transistor always has flow. Am I missing something here?

I actually don't understand the purpose of the whole output from pin 6 - doesn't the pin 7 output do the same thing (if you replace the relay with a motor?) Why the extra Q9 transistor? in the pin 6 arrangement?

After I understand these questions, maybe the outputs from pins 5 and 4 will make more sense too :)

Thanks!

  1. What are the functions of R14, R15, R20? Why was a 1k resistor chosen for r14/r15, but a 470 ohm for R20? Why these resistances at all vs a lesser or greater resistance? What would happen if they were entirely excluded from the circuit?

An output pin has a limited amount of current it can supply to a load.
It is recommended to keep it below 20ma for the Atmega238 controller (found on the UNO).
R14,15 and 20 keep the output current to a safe level.
Example: R14.
The load in this case is Q5. The base voltage is ~.7V.
Since the output on controller pin D12 can be as high as 5V (for an UNO) this leaves 5 - .7 = 4.3 volts for R14.
Resistance = Volts/Current.
R14 = 4.3V/20ma(max) = 215ohms.
R14 could be as low as 215 ohms, minimum, but 1K would reduce the pin output current to 4.3V/1k = 4.3ma, which is even more conservative.
Could you use 215? Yes, but 220 is a standard value so it would be a first choice to consider.
Ultimately, you need to limit the output current to less than 20ma as per the specification sheet.
Obviously the gain of Q5 needs to be high enough to have the 4.3ma (if using the 1k base resistor) base current to achieve the required Q5/LED D8 collector current.
R20 is a low value to swamp R21 current during normal operation. R21 current is only needed at reset/power up times.

What would happen if they were entirely excluded from the circuit?

Possible damage to the Arduino.

  1. Is R18 a pull down resistor? Why this denomination of resistance?

Yes, the purpose is to keep Q7 from turning on during Reset /power up conditions on the Arduino.
10k is a conservative value for a MOSFET transistor.
Could you use a lower value? Yes, but this would take more current from D7.

  1. Does R19 serve the same function as R14/R15/R20? Is this necessary with a FET? In researching transistors, my understanding was that there is almost no current draw from the gate pin of a FET anyway.

Yes, R19 limits the D7 output current while the input capacitance of Q7 charges. Note steady state MOSFET gate current is very very small but the initial inrush gate current can be large.

  1. What is the function of R21? Wouldn’t this arrangement result in a small current flowing into the base of Q9 and thus Q9 would always have flow between the emitter and collector? I don’t understand what the arduino pin 6 does in this arrangement, it just seems like if LOW the transistor has a small flow through it (+5v, through R21, into the base), and if HIGH it has even more flow through it (+5 from pin 6, through R20, into the base)… either way the transistor always has flow. Am I missing something here?

R21 turns on Q9 (which turns off Q8) during Reset/Power up on the Arduino.
This is desirable as you do not want the motor to turn during reset or power on times.
D6 is connected to label IRF540 which is connected to the left side of R20.
Since R20 is a small value, current through it can over ride the very low current from R21.
This is true when D6 is an output, however, when the Arduino powers up D6 is configured as an input therefore we need R21 to keep Q9 turned on during these times.

I actually don’t understand the purpose of the whole output from pin 6 - doesn’t the pin 7 output do the same thing (if you replace the relay with a motor?) Why the extra Q9 transistor? in the pin 6 arrangement?

D7 drives a Logic MOSFET (IRL) (one that can be driven directly from an Arduino pin).
D6 is controlling a non logic MOSFET (IRF) so Q9 is needed as a voltage level converter to Q8.

Resistor values may have to be changed slightly for different transistors used.
However, for 99+% of the time they should work.

Always check for transistor saturation in your final design to confirm proper switching.
For a BJT measure the Vce voltage.
For a MOSFET check Vds.
You want these to be as small as possible, <1V for most devices.

Ok all,

Sorry for the hiatus. I’m back and ready to get cracking on this project again now :slight_smile:

I ordered components on amazon and they should be arriving this afternoon.

here is the rough idea of what I want to do:

  1. At 5:00am, turn on a 110v outlet with 24v transformer plugged into it (to save power so that the
    transformer is not always on)
  2. At 5:00am, turn on sprinkler 1 (activate 24v solenoid)
  3. After 30min have elapsed, turn off sprinkler 1,
  4. Turn on sprinkler 2
  5. After 30 min have elapsed, turn off sprinkler 2
  6. Turn off 110v outlet with 24v transformer plugged into it

While above is going on, monitor water line pressure (once per second), if it drops below (TBD) value, turn off 110v outlet, pause for 5 minutes, recheck water line pressure, repeat until line pressure is back above (TBD) value, re-start watering cycle wherever it was paused

If at any point when an override start button is pressed, start the cycle immediately

If at any point a cancel button is pressed, stop the cycle immediately

What I have available to use:

110vac to 24vac wallwort transformer
24vac solonoid irrigation valves
2n3904 transistors
1N4001 diodes
1k Ohm resistors
10k ohm resisors
ds3231 based rtc module with battery backup
DC 5V Coil 7A 240VAC 10A 125VAC/28VDC 5 Pin SPST Power Relays
solderable pcb prototyping board
momentary push button switches
5vdc pressure transducer (output is linear, .5vdc at 0psi, 4.5vdc at 100psi)

See the attached diagram and let me know if i’m on the right track :slight_smile:

I’m sure that there are diagramming conventions that I don’t know, it looks messy to me :slight_smile:
Any advice as to how to better draw these things out?

Thanks all!

I’m starting to play with the coding and will post my sketch soon too.

schemeit-project.pdf (26.8 KB)

Schematic looks OK - but I'd connect the relay coils to the collectors of the drive transistors and ground the emitters..... in the emitters they'll only get at most supply - 0.7v

See you've sensibly used clamp diodes.

probably work anyway

regards

Allan

Hi, I agree with allanhurst, the relays need to be in the collector lead of the transistors. Switching the gnd side of a relay is common practice.

A good drawing for your schematic, you have tried not to crowd things, which is good, as you go your layout will improve with gnd wires along the bottom of the schematic and some sort of signal flow left to right.

You have labeled everything including pins, good, I understand until you start programming the actual arduino pins need not be numbered.

Doing well, the physical layout will be important so you keep mains voltage away from low voltage.

Tom..... :)

Thanks for pointing out the transistor wiring error, I have corrected it.

Can anyone take a crack at explaining the theory of why the coil needs to be on the collector side? My thinking was: 1. For a traditional switch it doesn't matter which side the switch is on 2.When voltage is applied to the base pin, it connects collector and emitter just like a switch therefore 3. It doesn't matter which side the coil is on, either current is flowing from collector to emitter or it isn't. 4. Typically (with household AC) I wire a switch into the hot side not the neutral side, so 5. I will put the transistor (switch) on the +, not the ground side

Obviously I'm missing something here :)

Thanks!

With the NPN or N-Channel MOSFET between the coil and Gnd, the Arduino only has to supply current from 5V to Gnd (NPN) or voltage (MOSFET) between 5V and Gnd to turn them on/off and allow current sink to Gnd.
With the parts between the + supply and the coil, the supplies all have be higher voltage to be able to turn on & off. PNP and P-Channel MOSFET are needed to turn on/off the current supply to the coil. If the coil is powered from 12V, then a 2nd transistor is needed to buffer the Arduino output to drive the 12V signal.

One part is easier/more efficient/less costly to use than 2, wouldn’t you agree?

Example:
AC power switch.jpg

For NPN transistors I’m trying to understand this chart:

E is emitter
B is base
C is collector

Applied voltages B-E junction bias B-C junction bias Mode
E < B < C Forward Reverse Forward-active
E < B > C Forward Forward Saturation
E > B < C Reverse Reverse Cut-off
E > B > C Reverse Forward Reverse-active

For my first example when the transistor was wired in WRONG:

When off:
I have 0v at B
I have 5v at C
I have 0v at E

so: E = B < C, cut-off or forward active, but with 0 at base forward active would still yield 0v at emitter

When on:

I have 0.7v at B
I have 5v at C
I have 0v at E

so: E < B < C, forward active, so some proportional amount of voltage would get through to emitter

For my corrected example when the transistor was wired in CORRECTLY:

When off:
I have 0v at B
I have 5v at C (is this right, do I have 5v here? there is no current flow, but does the 5v potential still
count as there?)
I have 0v at E

so: E = B < C, cut-off or forward active, but with 0 at base forward active would still yield 0v at emitter

When on:

I have 0.7v at B
I have 5v at C
I have 0v at E

so: E < B < C, forward active, so some proportional amount of voltage would get through to emitter

What am I missing or is incorrect here?
Something has to be wrong with my interpretation!!!

OP: Acknowledging you're experienced with mains wiring and safety, I can understand why you're switching the 110vAC, but for the newbies out there, I'd STRONGLY suggest leaving the 24v transformer wired securely and powered on, and only switching the ELV circuits.

This may eliminate some of the switching, as you on,y need to control the valve solenoids, and not the supply.

Time calculation issues!

So I got the components in the mail and have started playing with the rtc module (ds3231)

For my project, I need to be able to:

a) read the current time, and see if it is 5:00am to start the watering cycle b) read the time at the start of the watering cycle in seconds, read the current time in seconds, subtract these two numbers and end up with the elapsed seconds

The math is pretty simple if I can get the time converted into an integer which I think is my issue.

My plan was hour in 24 hour time*3600 + minute *60 + second

I am using the code below most of which I found online except for the timenowinseconds function which I wrote, and everything in the void loop which I've been using to test things out.

I am getting good numbers sometimes, but not all of the time...If I set the time to 1:10, all is well; if I set the hour to 12, it gets all crazy with hour*3600 yielding a negative number.

I think that the weirdness has something to do with the fact that I'm working with byte data not integers?

Also if there were something incorrect in the code the reads from the rtc I wouldn't pick up on it because I don't really understand it. In that function, what is the & 0x7f after *second? and similar after *hour? why no such term after *minute?

Here is the code:

include "Wire.h"

define DS3231_I2C_ADDRESS 0x68

// Function to Convert normal decimal numbers to binary coded decimal byte decToBcd(byte val) { return( (val/10*16) + (val%10) ); }

// Function to Convert binary coded decimal to normal decimal numbers byte bcdToDec(byte val) { return( (val/16*10) + (val%16) ); }

void setup() { Wire.begin(); Serial.begin(9600);

setDS3231time(00,16,12,3,8,10,16); // sets the initial time (seconds, minutes, hours, day, date, month, year) }

//function to set time of DS3231, must be after void setup() initiation of wire void setDS3231time(byte second, byte minute, byte hour, byte dayOfWeek, byte dayOfMonth, byte month, byte year) { // sets time and date data to DS3231 Wire.beginTransmission(DS3231_I2C_ADDRESS); Wire.write(0); // set next input to start at the seconds register Wire.write(decToBcd(second)); // set seconds Wire.write(decToBcd(minute)); // set minutes Wire.write(decToBcd(hour)); // set hours Wire.write(decToBcd(dayOfWeek)); // set day of week (1=Sunday, 7=Saturday) Wire.write(decToBcd(dayOfMonth)); // set date (1 to 31) Wire.write(decToBcd(month)); // set month Wire.write(decToBcd(year)); // set year (0 to 99) Wire.endTransmission(); } //function to read time of DS3231, must be after void setup() initiation of wire void readDS3231time(byte *second, byte *minute, byte *hour, byte *dayOfWeek, byte *dayOfMonth, byte *month, byte *year)// { Wire.beginTransmission(DS3231_I2C_ADDRESS); Wire.write(0x00); // set DS3231 register pointer to 00h Wire.endTransmission(); Wire.requestFrom(DS3231_I2C_ADDRESS, 7); // request seven bytes of data from DS3231 starting from register 00h *second = bcdToDec(Wire.read() & 0x7f); *minute = bcdToDec(Wire.read()); *hour = bcdToDec(Wire.read() & 0x3f); *dayOfWeek = bcdToDec(Wire.read()); *dayOfMonth = bcdToDec(Wire.read()); *month = bcdToDec(Wire.read()); *year = bcdToDec(Wire.read()); }

//function to get current time in number of seconds format int timenowinseconds() { byte second, minute, hour, dayOfWeek, dayOfMonth, month, year; int seconddec, minutedec, hourdec, timenowsecs;

readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);

timenowsecs = hour*3600 + minute*60 + second; return timenowsecs;

}

void loop() {

int rightnow = timenowinseconds();

byte second, minute, hour, dayOfWeek, dayOfMonth, month, year; readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);

int hourmath = hour*3600; int minutemath = minute*60; int nowinseconds = hourmath + minutemath + second;

Serial.print("hour: "); Serial.println(hour); Serial.print("hour * 3600: "); Serial.println(hourmath); Serial.print("minute: "); Serial.println(minute); Serial.print("minute * 60: "); Serial.println(minutemath); Serial.print("seconds: "); Serial.println(second); Serial.print("now in seconds: ");

Serial.print(nowinseconds); Serial.println(" as calculated outside of function");

Serial.print("now in seconds: "); Serial.print(rightnow); Serial.println(" function results"); Serial.println(); delay(5000); }

Hi,
jhm226, can you please post a copy of your sketch, using code tags?
They are made with the </> icon in the reply Menu.
See section 7 http://forum.arduino.cc/index.php/topic,148850.0.html

Thanks… Tom… :slight_smile:

Time calculation issues!

So I got the components in the mail and have started playing with the rtc module (ds3231)

For my project, I need to be able to:

a) read the current time, and see if it is 5:00am to start the watering cycle b) read the time at the start of the watering cycle in seconds, read the current time in seconds, subtract these two numbers and end up with the elapsed seconds

The math is pretty simple if I can get the time converted into an integer which I think is my issue.

My plan was hour in 24 hour time*3600 + minute *60 + second

I am using the code below most of which I found online except for the timenowinseconds function which I wrote, and everything in the void loop which I've been using to test things out.

I am getting good numbers sometimes, but not all of the time...If I set the time to 1:10, all is well; if I set the hour to 12, it gets all crazy with hour*3600 yielding a negative number.

I think that the weirdness has something to do with the fact that I'm working with byte data not integers?

Also if there were something incorrect in the code the reads from the rtc I wouldn't pick up on it because I don't really understand it. In that function, what is the & 0x7f after *second? and similar after *hour? why no such term after *minute?

Here is the code:

#include "Wire.h"
#define DS3231_I2C_ADDRESS 0x68

// Function to Convert normal decimal numbers to binary coded decimal
byte decToBcd(byte val)
{
  return( (val/10*16) + (val%10) );
}

// Function to Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
  return( (val/16*10) + (val%16) );
}

void setup()
{
  Wire.begin();
  Serial.begin(9600);
  
setDS3231time(00,16,12,3,8,10,16); // sets the initial time (seconds, minutes, hours, day, date, month, year)
}

//function to set time of DS3231, must be after void setup() initiation of wire 
void setDS3231time(byte second, byte minute, byte hour, byte dayOfWeek, byte
dayOfMonth, byte month, byte year)
{
  // sets time and date data to DS3231
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0); // set next input to start at the seconds register
  Wire.write(decToBcd(second)); // set seconds
  Wire.write(decToBcd(minute)); // set minutes
  Wire.write(decToBcd(hour)); // set hours
  Wire.write(decToBcd(dayOfWeek)); // set day of week (1=Sunday, 7=Saturday)
  Wire.write(decToBcd(dayOfMonth)); // set date (1 to 31)
  Wire.write(decToBcd(month)); // set month
  Wire.write(decToBcd(year)); // set year (0 to 99)
  Wire.endTransmission();
}
//function to read time of DS3231, must be after void setup() initiation of wire 
void readDS3231time(byte *second,
byte *minute,
byte *hour,
byte *dayOfWeek,
byte *dayOfMonth,
byte *month,
byte *year)//
{
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0x00); // set DS3231 register pointer to 00h
  Wire.endTransmission();
  Wire.requestFrom(DS3231_I2C_ADDRESS, 7);
  // request seven bytes of data from DS3231 starting from register 00h
  *second = bcdToDec(Wire.read() & 0x7f);
  *minute = bcdToDec(Wire.read());
  *hour = bcdToDec(Wire.read() & 0x3f);
  *dayOfWeek = bcdToDec(Wire.read());
  *dayOfMonth = bcdToDec(Wire.read());
  *month = bcdToDec(Wire.read());
  *year = bcdToDec(Wire.read());
}

//function to get current time in number of seconds format
int timenowinseconds()
{
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
  int seconddec, minutedec, hourdec, timenowsecs; 
  
  readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month,
  &year);

    
 timenowsecs = hour*3600 + minute*60 + second;
  return timenowsecs;

}

void loop()
{
   
int rightnow = timenowinseconds();


byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month,
  &year);
  
int hourmath = hour*3600;  
int minutemath = minute*60;
int nowinseconds = hourmath + minutemath + second;  

Serial.print("hour: ");
Serial.println(hour);
Serial.print("hour * 3600: ");
Serial.println(hourmath);
Serial.print("minute: ");
Serial.println(minute);
Serial.print("minute * 60: ");
Serial.println(minutemath);
Serial.print("seconds: ");
Serial.println(second);
Serial.print("now in seconds: ");

Serial.print(nowinseconds);
Serial.println(" as calculated outside of function");

Serial.print("now in seconds: ");
Serial.print(rightnow);
Serial.println(" function results");
Serial.println();
delay(5000);
}

I didn't go through your code with a fine-tooth comb, but are you setting / reading the 12/24 hour mode state in the DS3231 and handling the hours appropriately ?

DS3231 Register 09H in the chip datasheet...