Remote Control Panel for solar charger/inverter
Not all solar inverters come with ethernet connectivity and even when a remote panel is available it may require a physical rs232 cable to a panel mounted in a permanent in a building.
The project create a rs232 to wifi bridge and extend the control panel to an app on a phone to allow status monitoring and adjustment. This will not only allow the inverter to be accessible from anywhere but also eliminate the need for a cable.
Idea:
Progress:
The main loop on the device service the Firmata stream and request update from the UPS/inverter every 60 seconds, storre it in a json string and then send it to the APP via Firmata.sendString. It also check the WiFi status and tries to reconnect if disconnected.
/*********************************************************
* Main loop
*********************************************************/
void loop()
{
/* STREAMREAD - processing incoming messagse as soon as possible, while still checking digital inputs. */
if ( WiFi.status() == 3 )
{
while (Firmata.available())
{
digitalWrite(MKR_INFO,HIGH);
Firmata.processInput();
}
stream.maintain();
}
else
{
ledblink(MKR_LED, 500, 4);
connect_wifi();
}
//Serial.println(WiFi.status());
printWifiStatus();
delay(500);
if (last_request + wait_cycle < rtc.getEpoch()) //Run every wait_cycle seconds
{
last_request = rtc.getEpoch();
read_QMOD();
read_QPIGS();
//read_QPIWS();
String x;
root.printTo(x);
Firmata.sendString(x.c_str());
//digitalWrite(MKR_INFO,LOW);
Serial.println(x.c_str());
//if (connect_wifi()) {
// ledblink(MKR_LED, 500, 4);
azureHttpPOST(x);
JsonObject& root = jsonBuffer.createObject();
//}
}
};
The APP display the status information on tiles in a scrollable stackpanel. Top command bar has - close icon, reload for instantaneous update and two buttons to switch the led on the MKR1000 on and off (just for testing). The bottom command bar display the status, and has three buttons to change the mode of the UPS/inverter. (Automatic, Solar, Utility)
The meat of the UWP APP. Once a connection is created, the firmata StringMessageReceived is executed every time a message is received from the MKR1000, and displayed.
public class Target
{
public string INP_BAT_VOLT;
public string BATT_LEVEL_PERC;
public string OUT_LOAD_W;
public string OUT_LOAD_PERC;
public string INP_PV_VOLT;
public string INP_CHG_AMP;
public string INP_VOLT;
public string INP_FREQ;
public string OUT_LOAD_VA;
public string OUT_DISC_AMP;
public string OUT_VOLT;
public string OUT_FREQ;
public string MODE_MAINS;
public string I_SETTING;
public string CPU_VERSION;
public string CPU2_VERSION;
}
public async void Firmata_StringMessageReceived(
UwpFirmata caller, StringCallbackEventArgs argv)
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
string json = argv.getString();
if (IsValidJson(json))
{
try
{
newTarget = JsonConvert.DeserializeObject(json);
try
{
this.lva.Text = newTarget.INP_BAT_VOLT;
this.rva.Text = newTarget.BATT_LEVEL_PERC;
}
catch (Exception ex)
{
this.lva.Text = "?";
this.rva.Text = "?";
}
if (Int32.Parse(newTarget.BATT_LEVEL_PERC) < 100)
{ this.lico.Source = new BitmapImage(new Uri(base.BaseUri, "/assets/charged75.png")); }
if (Int32.Parse(newTarget.BATT_LEVEL_PERC) < 75)
{ this.lico.Source = new BitmapImage(new Uri(base.BaseUri, "/assets/charged50.png")); }
if (Int32.Parse(newTarget.BATT_LEVEL_PERC) < 10)
{ this.lico.Source = new BitmapImage(new Uri(base.BaseUri, "/assets/charged25.png")); }
if (Int32.Parse(newTarget.BATT_LEVEL_PERC) == 100)
{ this.lico.Source = new BitmapImage(new Uri(base.BaseUri, "/assets/charged100.png")); }
try
{
this.lva2.Text = newTarget.OUT_LOAD_W;
this.rva2.Text = newTarget.OUT_LOAD_PERC;
}
catch (Exception ex)
{
this.lva2.Text = "?";
this.rva2.Text = "?";
}
try
{
this.lva3.Text = newTarget.INP_PV_VOLT;
this.rva3.Text = newTarget.INP_CHG_AMP;
}
catch (Exception ex)
{
this.lva3.Text = "?";
this.rva3.Text = "?";
}
try
{
this.lva4.Text = newTarget.INP_VOLT;
this.rva4.Text = newTarget.INP_FREQ;
}
catch (Exception ex)
{
this.lva4.Text = "?";
this.rva4.Text = "?";
}
try
{
this.lva5.Text = newTarget.OUT_LOAD_VA;
this.rva5.Text = newTarget.OUT_DISC_AMP;
}
catch (Exception ex)
{
this.lva5.Text = "?";
this.rva5.Text = "?";
}
try
{
this.lva6.Text = newTarget.OUT_VOLT;
this.rva6.Text = newTarget.OUT_FREQ;
}
catch (Exception ex)
{
this.lva5.Text = "?";
this.rva5.Text = "?";
}
btnEcho.IsChecked = false;
btnEcho.IsEnabled = true;
}
catch (Exception ex) //some exception with json data
{
this.txtStatus.Text = "Invalid data";
}
}
else
{
this.txtStatus.Text = "Invalid data";
}
});
}
I am not a developer and the code was put together from many examples and questions I found online. It works but needs a lot of refinement and debugging before it will be a stable and reliable solution. The data is send to the Azure IOT platform but I could not find working UWP examples in order to make the APP read from the cloud when one is not on the local network. This will make the app a lot more useful.
Comments