This is the final update in the solar panel project series for now. Hopefully by now you must be quite bored with the updates because I am sure no one is going to build a project that is exactly same as mine. But in case you are following along, you might have picked up some things that you did not know earlier or may be you found somethings that you could have done differently. The reason I am writing is to keep a journal of things. While I fixed most of the issues with my setup, I still had a couple of problem that don’t happen frequently which makes it difficult to debug. In this post I will discuss the problems and my solutions. May be there are better solutions, but these are ones that I could come up with. A reader was lamenting that my posts are too short so I let this post go as long as it needs to be. So enjoy :).


One of the problems I was facing with my setup is that the android app sometimes crashes and I am not sure why. The android app is the brains that determines how to manage power coming to the house. It has the logic which tracks the battery percentage, solar power, load in the house, grid status etc and diverts energy so as to maximize solar power usage while keeping the battery charged to a good state. As you can imagine, once the app crashes, the entire system could be in a not so desirable state.


Take for instance, the app thinks there is too much solar power and the battery is fully charged, so it disconnects grid and diverts all power from solar + battery to the load (house). At this point, the app crashes. Then the relay will be stuck in the mode where solar + battery is connected to load forever. If I did not notice and relaunch the app, this mode would continue on and after sunset, the battery will be delivering all the power and drain over night. Not a good situation to be in especially if we are out traveling.


I had some defenses against these kind of issues from the very beginning of the project. First, the arduino board (which never crashes by the way), will automatically turn on the relay that connects the grid to load if the android app stops communicating with it for more than a certain amount of time. Second, it will connect to grid if the battery is below critical voltage assuming for some reason the android app did not detect it. So there will never be a case where the battery will be drained even if there is a loss in communication or android app crashes.


// If there is no bluetooth communication for this many seconds, connect to grid
#define BLUETOOTH_TIMEOUT 10 * 60
// If battery voltage is less than this much, connect to grid
#define CRITICAL_VOLTAGE  21.0

void loop() {
  sensors.update();
  if (bluetooth.available() > 0) {
    handleBluetoothCommand();
  }

  if (!relay.isOn() 
      && now() - lastBluetoothCommunicationTime > BLUETOOTH_TIMEOUT) {
    Serial.println("No communication from bluetooth, connecting to grid");
    relay.turnOn();
  }

  if (!relay.isOn() && sensors.getBatteryVoltage() < CRITICAL_VOLTAGE) {
    Serial.println("Battery below critical voltage, connecting to grid");
    relay.turnOn();
  }
}


The second level of defense was in the android app itself. I made sure that the app restarts automatically after a crash. I marked the app as a foreground service in AndroidManifest.xml so it can continuously run on the android tablet, even if it is in the background. Moreover foreground services receive more resources that they otherwise would get.


<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />


In addition, I return START_REDELIVER_INTENT in onStartCommand of the service. For those of you who don’t know START_REDELIVER_INTENT causes the app to be restarted by the android system if the app crashes.

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
  super.onStartCommand(intent, flags, startId);
  String deviceAddress = intent.getStringExtra(Constants.INTENT_EXTRA_BLUETOOTH_DEVICE_ADDRESS);
  startBluetoothThread(deviceAddress);
  return START_REDELIVER_INTENT;
}


With these defenses in place, the app is pretty stable, and restarts quickly after a crash. Defensive coding is one aspect, but we do have to find the root cause of the crash too. I wanted to know why the app crashed in the first place so I can fix my code. So I installed Crashlytics. I found a few edge case bugs and fixed them. Now the app sometimes gets killed by android system (out of resources perhaps), but not because of any bugs in the code. The most recent bug I squashed was detected by Crashlytics and delivered this log.

Fatal Exception: io.flutter.plugins.firebase.crashlytics.FlutterError: MissingPluginException(No implementation found for method listen on channel plugins.flutter.io/firebase_firestore/document/...). Error thrown Instance of 'ErrorDescription'.
       at MethodChannel._invokeMethod(platform_channel.dart:294)
       at EventChannel.receiveBroadcastStream.<fn>(platform_channel.dart:637)


However, my problems did not end there. The bluetooth module HC-05 that I used in the arduino setup was misbehaving. I use bluetooth to communicate between arduino and android. The arduino board just collects raw data from the sensors and the android app uses the data to determine what to do next. If the bluetooth communication fails, the app cannot make any decisions. The problem was that HC-05 would go into a weird state every once in a while and never recovers itself. It happens infrequently so it was hard to discover the issue. The only fix is to cycle the power to the board.


What triggers in your mind when I say cycle the power? A relay perhaps, or more accurately a transistor. Which is precisely what I did. I added a transistor to the board and it acts as an electronic switch using which I can turn off the bluetooth device and turn it back on when it misbehaves. Below you will see the circuit diagram I initially came up with, but it came with its own set of problems which I will explain shortly. First lets look at the circuit diagram.



In the arduino code I detect if there is no communication from android over bluetooth for a certain amount of time, I assume the HC-05 is in a bad state and power cycle it with the following code.

// If there is no bluetooth communication for this many seconds, reset HC-05
#define RESET_BLUETOOTH_TIMEOUT 60
#define BLUETOOTH_RESET_PIN  14   // same as A0

void resetBluetooth() {
  Serial.println("Turning off bluetooth");
  pinMode(BLUETOOTH_RESET_PIN, OUTPUT);
  digitalWrite(BLUETOOTH_RESET_PIN, LOW);
  delay(5000);
  Serial.println("Turning on bluetooth");
  digitalWrite(BLUETOOTH_RESET_PIN, HIGH);
  delay(1000);
  resetBluetoothTime = now();
}

void loop() {
  if (now() - resetBluetoothTime > RESET_BLUETOOTH_TIMEOUT) {
    resetBluetooth();
  }
}


Soon another problem started. While the base current into the transistor is small enough, the arduino board seems to be burdened. My sensor readings were all over the place depending on how much base current is flowing. The base current keeps changing as the load (from HC-05) changes and the load changes because the HC-05 has blinking LEDs and radios which pull more and less power at different times. Look at the jagged readings in the graph below after using a transistor.



So what is the solution? Well, we need a device with high input impedance and low output impedance. Op-amp of course. Then I added an op-amp to the circuit to limit the current draw from arduino. Arduino drives op-amp as a buffer stage. The op-amp sends the signal to a transistor without loading the arduino. Finally the transistor controls the power to bluetooth. Here is the updated circuit diagram.



That finally reduced the jitters in the output. Now my setup is very stable. For the last 2 months, there has not been a single instance where the app lost data. I still have a few ideas to improve it both in software and hardware but I will give those updates in future if I get to them. If you have followed the solar panel project updates I have been giving for the last couple of months, you know why this has been such a long running project. I had to make all these minor fixes to get it to perfection. Without making the system accurate and stable, I cannot collect all the sensor data and make accurate predictions on how much money the panel is saving me. In a couple of weeks I will write a post on how many units of power my solar panel saved me. Stay tuned.