Nano-based solar tracker with lcd hangs or crashes

Hi, I have a problem with homemade solar tracker based on Arduino Nano. It works for few hours and then Nano "hangs" and doesn't work until I unplug power (LCD displays nothing or some glitches).
First of all, I've tried both original Nano, Uno and Nano clones - the same behavior. Tracker contains Nano, 20x4 LCD (JHD204A-BW, compies with HD44780), one single relay module and one dual relay module and one dpdt relay (singe relay module controls dpdt relay which changes polarity to rotating motor and dual relay module switches power to motor). There are 4 buttons, but they aren't used and were DS1302 RTC module, but it didn't worked for me (library od module were broken - either it reseted time or didn't went while unplugged). For light sensing I use 4 photoresistors (two for each direction). Whole setup is powered by 5V 3A power suplly. If you want some schematic I'll try draw something.

Here is code I use; function sprawdz_noc() checks if values (voltage) from photoresistors are smaller than thresholds (variables noc and margines), function obrot() is used to rotate panels - it read values from photoresistors, and if required switches direction, powers on motor and keep reading values until they smaller than threshold (var. roznica), then it powers off motor. Function powrot() is used to rotate panels to "full east" position and wypisz() is for printing
photoresistor values on LCD.
Rotating is done every ~15 minutes, if there isn't night and if is night and previous was day it should rotate east.

#include <LiquidCrystal.h>
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
int zach1 = 0, zach2 = 0, wsch1 = 0, wsch2 = 0;
int roznica = 30, margines = 30;
int noc = 80;
int sekundy = 1000;
bool p_noc = false;

void setup ()
{
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(13, OUTPUT);
  digitalWrite(9, HIGH);
  digitalWrite(10, HIGH);
  digitalWrite(13, LOW);
  lcd.begin(20, 4);
  Serial.begin(9600);
}
void loop ()
{
  lcd.setCursor(0, 2);
  String ile_zostalo = "";
  int minuty = (900 - sekundy) / 60;
  ile_zostalo = "Zostalo: " + String(minuty) + " m " + String(900 - sekundy - (minuty * 60)) + " s";
  lcd.print(ile_zostalo);
  wypisz();
  if (sekundy >= 900)
  {
    sekundy = 0;
    if (!sprawdz_noc())
    {
      obroc();
    }
    else
    {
      lcd.setCursor(0, 4);
      lcd.print("OBROT: NIE ");
      if (!p_noc) powrot();
    }
  }

  sekundy += 1;
  delay(1000);
}

bool sprawdz_noc()
{
  zach1 = analogRead(A7);
  zach2 = analogRead(A6);
  wsch1 = analogRead(A5);
  wsch2 = analogRead(A4);
  int za = (zach1 + zach2) / 2;
  int ws = (wsch1 + wsch2) / 2;
  if ((za < noc + margines) && (ws < noc + margines))
  {
    lcd.setCursor(0, 1);
    lcd.print("Noc: " + String(noc) + " TAK");
    p_noc = true;
    return true;
  }
  else {
    lcd.setCursor(0, 1);
    lcd.print("Noc: " + String(noc) + " NIE");
    p_noc = false;
    return false;
  }
}


void obroc()
{
  int wsch, zach;
  zach1 = analogRead(A7);
  zach2 = analogRead(A6);
  wsch1 = analogRead(A5);
  wsch2 = analogRead(A4);

  zach = (zach1 + zach2) / 2;
  wsch = (wsch1 + wsch2) / 2;
  int rozn = zach - wsch;
  if (rozn > 0)
  {
    if (rozn > roznica)
    {
      lcd.setCursor(0, 4);
      lcd.print("OBROT: ZACH");
      digitalWrite(13, LOW);
      digitalWrite(9, LOW);
      digitalWrite(10, LOW);
      obroc();
    }
    else
    {
      digitalWrite(13, LOW);
      digitalWrite(9, HIGH);
      digitalWrite(10, HIGH);
      lcd.setCursor(0, 4);
      lcd.print("OBROT: NIE ");
      return;
    }
  }
  else
  {
    if (rozn * (-1) > roznica)
    {
      lcd.setCursor(0, 4);
      lcd.print("OBROT: WSCH");
      digitalWrite(13, HIGH);
      digitalWrite(9, LOW);
      digitalWrite(10, LOW);
      obroc();
    }
    else
    {
      digitalWrite(13, HIGH);
      digitalWrite(9, HIGH);
      digitalWrite(10, HIGH);
      lcd.setCursor(0, 4);
      lcd.print("OBROT: NIE ");
      return;
    }
  }
}


void powrot()
{
  digitalWrite(13, HIGH);
  digitalWrite(9, LOW);
  digitalWrite(10, LOW);
  for (int i = 0; i < 180; i++)
  {
    delay(1000);
  }
}

void wypisz()
{
  zach1 = analogRead(A7);
  zach2 = analogRead(A6);
  wsch1 = analogRead(A5);
  wsch2 = analogRead(A4);
  String z1 = "", z2 = "", w1 = "", w2 = "";
  if (zach1 < 10) z1 = "000" + String(zach1); else if (zach1 < 100) z1 = "00" + String(zach1); else if (zach1 < 1000) z1 = "0" + String(zach1); else z1 = String(zach1);
  if (zach2 < 10) z2 = "000" + String(zach2); else if (zach2 < 100) z2 = "00" + String(zach2); else if (zach2 < 1000) z2 = "0" + String(zach2); else z1 = String(zach2);
  if (wsch1 < 10) w1 = "000" + String(wsch1); else if (wsch1 < 100) w1 = "00" + String(wsch1); else if (wsch1 < 1000) w1 = "0" + String(wsch1); else w1 = String(wsch1);
  if (wsch2 < 10) w2 = "000" + String(wsch2); else if (wsch2 < 100) w2 = "00" + String(wsch2); else if (wsch2 < 1000) w2 = "0" + String(wsch2); else w1 = String(wsch2);
  String out = w1 + " " + w2 + " " + z1 + " " + z2;
  lcd.setCursor(0, 0);
  lcd.print(out);
}

And as I said before it works few times and hangs/crashes blanking LCD or displaying some garbage. Any ideas what could be wrong? Is that hardware or program related?

Use of the String class can lead to memory corruption.

Your post was MOVED to its current location as it is more suitable.

Do you mean "String name = value" or "String(integer)" or both? If "String(integer)" I've tried doing sth like z1 = "000"+zach1; etc but this didn't worked for me (probably to much java :stuck_out_tongue: )

Anywhere you use the word String or manipulate String variables is risky on an Arduino with little RAM, like a Nano. Running for a while before crashing is a classic sign of heap fragmentation.

The probability of String-related crashes is vastly reduced if you replace this

  ile_zostalo = "Zostalo: " + String(minuty) + " m " + String(900 - sekundy - (minuty * 60)) + " s";
  lcd.print(ile_zostalo);

With this:

lcd.print("Zostalo: ").
lcd.print(minuty);
lcd.print("m ");
lcd.print(900 - sekundy - (minuty * 60));
lcd.print("s");

If you are interested, here is an explanation of why the String class is to be avoided unless you know what you are doing.

I'll try this if lcd.print(...) won't override whole line and I won't get something like "s5stalo: " instaead of "Zostalo: 13m 45s"

The beauty of Arduino is that you can try something, and in just a minute or two, see the result!

  int sekundy = 1000;
  .
  .
  .
  sekundy += 1;
  delay(1000);

An int will hold a maximum value of 32767, then will overflow to -32768. This will occur after a little less than 9 hours of runtime.

Value 1000 of sekundy (seconds) is for immediate start rotating after power on and when it checks if 15 minutes passes (900 seconds) it resets "timer" and starts from begining

Ok, I didn't see where you had set sekundy to 0.

Okay - I've changed all String for char var[20] but it still sometimes hangs and clears LCD. Any other ideas?

Please post your full revised sketch in a new reply

As one who has a running Solar panel tracker that uses SPA I'll just say that without stepper motors or an encoder on regular DC motors, you will never point the panel accurately and repeatedly.
I initially used DC motors but the change in temperature really messes up repeatability without some form of feedbackdevice such as encoder or resolver.

This doesn't have to be acurate, because it will be only east-west rotatable :wink: And it will have photoresistors as sensors of sun position, so it should produce more kW than stationary panels directed south (in my case). For example on east photoresistor is 3V (~600 reading) and on west is 4V (reading about 800) it means that it have to rotate to west until east ~= west

Using photocells can be a problem in cloud cover, which by the way doesnt stop modern panels from making power. (its not just visible light that converts) .
At least by using a clock based tracker, you know where the panel 'needs' to be pointing.
Failure mode on photocells will either run it fully west or east on failure.

I know. And I know that is autumn and probably in december will be snow. It isn't main power source and if it will produce 6kWp instead od 7,4kWp will be still better producing this 6kWp all day :wink: 7,4kWp produced about 35-38kW without rorating (or rotating 2-3 times a day) and about 45-48kW when I've tested tracker. So it is about 6-7 hours of full power

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.