Monday, 24 April 2023

First on-grid experience. 3.5 units (KWh) generated. 2 units exported to the grid.

 While in Mayo I will continue to experiment with off-grid systems in Shannon it makes less sense. Especially with two lodgers.

I had bought two grid-tied 1.5KW PV inverters some time ago (Hypontech) and finally got round to finding an electrician who would sign-off my ESB Networks FC6 form. After submitting it (4 pages) by e-mail (along with the conformity document) to ESB Networks on Tues 4th April. Then on Thurs 13th April I got a terse e-mail reply from ESB Networks stating "the details provided have been noted and updated on our records.". I was going to wait the mandatory working 20 before connecting up my system.

This is explained in their document on page 13: https://www.esbnetworks.ie/docs/default-source/publications/conditions-governing-the-connection-and-operation-of-micro-generation-policy.pdf

"If ESB Networks determines that for a given Type-Test /Confirmation of Table 3 Settings certificates, further clarification or information is required, then ESB Networks shall: 

1. Instruct the customer within 20 working days from receipt of both the Type-Test certification & Confirmation of Table 3 Settings, to refrain from installation or suspend installation and commissioning if already begun. 

2. Require that the customer contact the installer/equipment supplier or test laboratory as appropriate to obtain any additional information or clarification"

But after e-mailing my electricity supplier Community Power they informed me on Tues 18th April that "On 13-April, ESBN sent us notification that you are set to a MaximumExportCapacity="2". So from this it appears I was good to go, After accepting the T&C document from Community Power about generation payments.

So starting on Friday 21st April 2023 I connected my unit to the 4x 230 Wp PV panels and the mains in the kitchen and started to generate grid tied power.







As of last night Sun 23rd April 2023 I had generated 2.3 KWh that day (5 KWh accumulated) and to date have exported 2 KWh to the grid.






So overall I'm very impressed by the performance of the grid-tied inverter and have decided to remove my off grid system form my kitchen at Tola!

Less impressed with the Hypontech website and lack of functioning WiFi dongle for cloud monitoring.

But my Sonoff mains WiFi switch provides power monitoring instead.


Which via MQTT feeds into my Smart Home app:









Thursday, 5 May 2022

LS2024B Landstar PV charge controller LoRaWAN monitor

This has evolved over some months the latest device is based on a Heltec AB-02 CubeCell Dev Board Plus. (https://heltec.org/project/htcc-ab02/)




There is a  18650 3.6v (nominal) 2600mAh cell connected directly to this board and a RS-485 isolated interface board (Isolated TTL to RS485 3.3V 5V Converter Board Module Serial for Raspberry https://www.ebay.ie/itm/202608485280)  whose VCC (VDD) , TXD (TX2), RXD (RX2)and GND (GND next to VEXT) pins are connector to the AB-02 board as indicated in brackets (NOTE: GND pin next to VIN has a faulty connection).





The RJ-45 lead from the LS2024B provides A, B, GND and 7.5v


The 7.5v from the RJ45 (pin 1 blue/white) was dropped down to 6.66v using three signal diodes in series and then fed into the VS* :Solar input pin(5.5V~7V) on the AB-02.

The GND, A and B pins of the RS485 interface board were wired to pins 8 (brown), 6 (orange) and 4 (blue) respectively of the RJ45 cable (see drawing above)

To support access to the AB-02 debug output it'sTX1/RX1/GND were connected to a flying USB cable (without the +5v wire connected).


The sketch for the board is shown below:

#define SKETCH_NAME     "Cubecell_PV_esmart3_monitor_slan-032_v1.21"
#define VERSION         "Tues 3rd May 2022 21:24"

#define LOOP_DELAY_MSECS  1000 // 1 sec
#define MAX_TICKS         30 // 5 mins (very approx)

// https://www.thethingsnetwork.org/forum/t/cubecell-ab02-oled-power-off-for-deep-sleep/45008

/* Version history
 * 
 * v1.20   Fri  14th Jan 2022 11:34    Fixed lack of transmission and added display button (slan-032 variant)
 * v1.19 Thurs  13th Jan 2022 13:23    slan-032 version
 * v1.18 Thurs  13th Jan 2022 13:23    Abandoned leaky buckets not precise enough for straight averaging
 * v1.17 Weds   12th Jan 2022 22:47    Added 15/16 leaky bucket and continuous measurements
 * v1.16     Sun 9th Jan 2022 23:12    Created slan=032 CubeCell for final build and added display.stop()/connect() and comms. errs 
 * v1.15     Sun 9th Jan 2022 21:28    Added display support and tested with USB programming cable so no 5v
 * v1.14     Sun 9th Jan 2022 18:10    Added rx checksum checking as rx data was bad when powered from LS20124B
 * v1.13     Sun 9th Jan 2022 17:01    Now works by having RE/DE enabled all the time and discarded echoed 8 byte commands 
 * v1.12     Sun 9th Jan 2022 16:23    Changed back to using my Modbus tx/rx code but with RE and DE control 
 * v1.11     Sun 9th Jan 2022 15:30    Changed to using ModbusMaster library and steveiboy code
 * v1.10     Sun 9th Jan 2022 15:30    Changed to using MAX485 module 
 *  v1.9     Sun 9th Jan 2022 14:55    Changed sketch name to "Cubecell_PV_epever_monitor_slan-031_v1.9" 
 * 
 */
/*
    This code is for reading data from an EPEver LandStar B ( LS1024B )
    SOlar PV MPTT Charge controller is bassed on code posted to a
    forum topic:
    
    https://forum.arduino.cc/t/epsolar-solar-charge-controller-monitoring-system/895450/3
    
    by steveiboy. I have kept those he credited in this description :)
    
    However, my code is different in five ways:
    
    1) It used a Heltec CubeCell AB02 rather than an NodeMCU clone.
    
    2) It sends its data via LoRAWAN to The Things Network rather than via WiFi to an MQTT Broker.
    
    3) It uses an isolated RS485 interface board rather than a MAX485 board.
    
    4) It communicates in raw byte rather than us teh excellent ModbusMaster library
    5) It benefits from having a second serial port that it can use.
    Both modules are powered using the (in my case) 7.5 Volt supply-voltage
    that is available at the RJ45 port. If you're using another MCU make 
    sure, the onboard voltage-regulator can handle the 7.5 volts from the EPEver.
    
    To avoid the need of a level-shifter, the max485 module is powered only
    with 3V3 from the CubeCell, which works for me, but YMMV.
    
    Power-consumption is roughly XXX during Deep-Sleep, mostly due to the
    onboard leds, I guess. When running, the power-demand gets up to about
    YYY for 3-4 seconds.
    
    Connections:
    https://resource.heltec.cn/download/CubeCell/HTCC-AB02/HTCC-AB02_PinoutDiagram.pdf
            MAX485         NodeMCU   Heltec CubeCell Dev-Plus HTCC-AB02
            DI              TX              UART_TX2  TX2 (30)
            RO              RX              UART_RX1  RX1 (17) (NOTE: TX1 used to output debug!)
            VCC             3V3 !!!
            GND             GND

            So looking at the Epever with the COM port at the right labelled COM and the pins from
            left to right are?   1 2 3 4 5 6 7 8 
            
            
         EpEver RJ45                        MAX485      NodeMCU
        pin1  +7.5 V       org-wht                       Vin
        pin8  GND          brn                           GND
        pin6  RS-485-A     grn               A
        pin4  RS-485-B     blu               B
    connect DE (Max485) with a pull-down resistor (e.g. 6k7) to GND,
    to hold that line down in Deep-Sleep to lower power consumption
    connect D0 (NodeMCU) with reset (NodeMCU) for DeepSleep wake-up to work
    connect D6 (NodeMCU)and D7 (NodeMCU) to enable debug-mode. this
    sets the sleep duration to only 10 seconds
    some datasheets list different pinouts for the RJ45 jack!  swap A<->B if
    connection fails. Check voltage-level and -polarity before connecting!
    I'm excessively using the union-struct trick to map buffer-data
    to structs here. Most of the defines for the data-locations
    are for reference only and not actually used in the code
    I got loads of info for this from:
        https://www.eevblog.com/forum/projects/nodemcu-esp8266-rs485-epever-solar-monitor-diy/
        http://4-20ma.io/ModbusMaster
    For taking the data to grafana, have a look here:
        https://github.com/glitterkitty/mqtt-mysql
*/

#include "Arduino.h"
#include "LoRaWan_APP.h"
//#include <ModbusMaster.h>
// For a connection via I2C using the Arduino Wire include:
#include <Wire.h>               
#include "HT_SH1107Wire.h"
// Handy for seeing missing Modbus bytes
boolean   debug  = false;  // For RS-485 comms.
boolean   debug2 = false; // For LoRa comms.
boolean   info = false; // For status messages
//SH1107Wire  display(0x3c, 500000, SDA, SCL ,GEOMETRY_128_64,GPIO10); // addr, freq, sda, scl, resolution, rst
extern SH1107Wire  display; // Handled by "LoRaWan_APP.h"
// Pins
//
// ./CubeCell-Arduino/cores/asr650x/board/inc/board-config.h
// ./CubeCell-Arduino/cores/asr650x/board/inc/gpio.h
// ./CubeCell-Arduino/cores/asr650x/cores/ASR_Arduino.h
// ./CubeCell-Arduino/cores/asr6601/base/ASR_Arduino.h
// ./CubeCell-Arduino/variants/CubeCell-ModulePlus/pins_arduino.h
// 


#define MAX485_DE       GPIO2 // D2  // data or
#define MAX485_RE       GPIO1 // D1  //      recv enable
void preTransmission()
{
  digitalWrite(MAX485_RE, 0 /* 1 */); // Receiver Enable LOW to receive
  digitalWrite(MAX485_DE, 1); // Driver Enable HIGH to transmit
 // digitalWrite(LED, LOW);
}
void postTransmission()
{
  digitalWrite(MAX485_RE, 0); // Receiver Enable LOW to receive
  digitalWrite(MAX485_DE, 1 /*0*/);  // Driver Enable HIGH to transmit
  //digitalWrite(LED, HIGH);
}

/* OTAA para*/
//uint8_t devEui[] = { xxx.. };  // MSB (V3 slan-031)
//uint8_t appEui[] = { xxx.. };  // MSB
//uint8_t appKey[] = { xxx.. }; // MSB
uint8_t devEui[] = { yyy..};  // MSB (V3 slan-032)
uint8_t appEui[] = { yyy.. };  // MSB
uint8_t appKey[] = { yyy..}; // MSB
  
/* ABP para for xxxxxx */
uint8_t nwkSKey[1];
uint8_t appSKey[1];
uint32_t devAddr = 0; // <-- Change this address for every node!
uint32_t appTxDutyCycle = (5 * 60 * 1000); // the frequency of readings, in milliseconds (set to 5 mins)
uint16_t userChannelsMask[6]={ 0x00FF,0x0000,0x0000,0x0000,0x0000,0x0000 };
LoRaMacRegion_t loraWanRegion = ACTIVE_REGION;
DeviceClass_t  loraWanClass = LORAWAN_CLASS;
bool overTheAirActivation = LORAWAN_NETMODE;
bool loraWanAdr = LORAWAN_ADR;
bool keepNet = LORAWAN_NET_RESERVE;
bool isTxConfirmed = LORAWAN_UPLINKMODE;
// 2022/1/3 Had to add this to get code to compile. I guessed at value 10
uint8_t confirmedNbTrials = 10;
uint8_t appPort = 10; // for LS2024B payload
#define TIMEOUT 10//time in ms
uint8_t serialBuffer[256];
int size;







char buf[256];
// Compute the MODBUS RTU CRC
uint16_t ModRTU_CRC(uint8_t *  buf, int len)
{
  uint16_t crc = 0xFFFF;
  if (debug) Serial.printf("Entering ModRTU_CRC()..\n");
  
  for (int pos = 0; pos < len; pos++) {
    crc ^= (uint16_t)buf[pos];          // XOR byte into least sig. byte of crc
  
    for (int i = 8; i != 0; i--) {    // Loop over each bit
      if ((crc & 0x0001) != 0) {      // If the LSB is set
        crc >>= 1;                    // Shift right and XOR 0xA001
        crc ^= 0xA001;
      }
      else                            // Else LSB is not set
        crc >>= 1;                    // Just shift right
    }
  }
  // Note, this number has low and high bytes swapped, so use it accordingly (or swap bytes)
  if (debug) Serial.printf("..leaving ModRTU_CRC()\n");
  return crc;  
}

/*
 * Example
 * TX:01 04 31 1d 00 01 af 30
 * RX:0x01, 0x04, 0x02, 0x04, 0xb0, 0xba, 0x44
 */

int readFromRegister( uint16_t address, int size, uint16_t *  buffer ) 
{
  uint8_t   txBuffer[ 16 ];
  uint16_t  crc; 
  
  uint8_t   rxBuffer[ 16 ];
  uint8_t   incomingByte;
  int       rxCount; 
  int       status = 0;

  if (debug) Serial.printf("Entering readFromRegister()..\n");
  if (size > 16) return -1;
  
  // Read data start ing at registerAddress
  txBuffer[ 0 ] = 0x01; // read coils command
  txBuffer[ 1 ] = 0x04; // ??
  txBuffer[ 2 ] = (address >> 8);
  txBuffer[ 3 ] = (address & 0x00FF);  
  txBuffer[ 4 ] = 0;
  txBuffer[ 5 ] = size;  
  crc = ModRTU_CRC( txBuffer, 6 );
  txBuffer[ 6 ] = (crc & 0xFF);  
  txBuffer[ 7 ] = ((crc >> 8) & 0xFF);
     
  if (debug) Serial.printf("\nRequesting %d bytes starting at 0x%04x\n", 
                ((txBuffer[4] * 256) + txBuffer[5])*2,  
                ((txBuffer[2] << 8) + txBuffer[3]) );
  if (debug) {
    for (int i=0; i<8; i++) Serial.printf("%02x ", txBuffer[i] );
    Serial.printf("\n" ); 
  }
  preTransmission();  
  //delay(200);         
  Serial1.write( txBuffer, 8 );
  //delay(1000);
  postTransmission();
  delay(100);
  
  int ignoreFirstNByte = 1; // If not expecting tx chars to be echoed back don't set to 8
  boolean ignoreLastByte = true;
  
  rxCount = 0;
  while ((Serial1.available() > 0) && (rxCount < 50)) {
    // read the incoming byte:
    incomingByte = Serial1.read();
    if (debug) Serial.printf( "%02x, ", incomingByte);
    rxBuffer[ rxCount ] = incomingByte;
    if (ignoreFirstNByte > 0) {
      if (debug) Serial.printf( "WARNING: Ignoring first byte of message 0x%02x\n", incomingByte);
      ignoreFirstNByte--;
      continue; // ignore first byte
    }
    
    if ((rxCount == 0) && (incomingByte != 1)) {
      // Expected response tp be of the form :
      // RX:0x01, 0x04, 0x02, 0x04, 0xb0, 0xba, 0x44
      Serial.printf( "ERROR: Response started with 0x%02x and not 0x01\n", incomingByte);
      status = -1;      
    }
     if ((rxCount == 1) && (incomingByte != 4)) {
      // Expected response tp be of the form :
      // RX:0x01, 0x04, 0x02, 0x04, 0xb0, 0xba, 0x44
      Serial.printf( "ERROR: Response second byte was 0x%02x and not 0x04\n", incomingByte);
      status = -1;      
    }
    
    if ((rxCount == 2) && (incomingByte != (size * 2))) {
      // Expected response to be of the form :
      // RX:0x01, 0x04, 0x02, 0x04, 0xb0, 0xba, 0x44
      Serial.printf( "ERROR: Response had bytes size of 0x%02x and not 0x%02x as expected\n", incomingByte, (size * 2));
      status = -1;
    }    
       
    rxBuffer[ rxCount ] = incomingByte;
    rxCount++;
  }
  delay(200);
  if (rxCount >= 50) {
      if (debug) Serial.printf( "\nERROR: Terminated loop because rcCount(%d) >= 50 bytes\n", rxCount);    
  }
  
  if (ignoreLastByte) {
      if (debug) Serial.printf( "\nWARNING: Ignoring last byte of message 0x%02x\n", incomingByte);
      rxCount--;
  }
  
  if (rxCount != (5 + (2 * size))) {
    // e.g. [01], [04], [02], [00, 01], [78, f0], 
    Serial.printf( "ERROR: Expected %d bytes but received %d\n", (5 + (2 * size)), rxCount );
    status = -1;    
  }
  // If all good let's put the data on the return array
  if (status == 0) { // wasn't set to -1 by a failure
    int j = 0;
    for (int i = 3; i< 3+(2 + size); i += 2) {
      buffer[ j++ ] = (rxBuffer[ i+1 ] << 8) + rxBuffer[ i ];
    }
    status = size;
  }

  if (info) Serial.printf( "INFO: rxCount=%d\n", rxCount);
  
  if (rxCount > 2) {
    // Finally let's check the rx checksum!
    uint16_t rx_crc = ModRTU_CRC( rxBuffer, rxCount-2 );
    uint16_t actual_crc = ((uint16_t)rxBuffer[ rxCount-1  ] << 8) | (uint16_t)rxBuffer[ rxCount-2 ];
    
    if (info) Serial.printf( "INFO: Expect 0x%04x rx checksum and received 0x%04x\n", rx_crc,  actual_crc);
/*
    if (debug) Serial.printf("displaying checksums on OLED display..\n");  
    
    vExtOn();
    delay(100);
    
    display.connect();
    delay(100);    
    
    display.clear(); 
    String row1 = String(rx_crc);  
    String row2 = String(actual_crc);        
    display.drawString(0, 0,  row1 );
    display.drawString(0, 20, row2);
    display.display();
    delay(2000);
    display.clear();

    display.stop();
    delay(100);
    
    vExtOff();     
    delay(100);    
    if (debug) Serial.printf("..displayed\n");
*/          
    if (actual_crc != rx_crc) {
      Serial.printf( "ERROR: Expected 0x%04x rx checksum but received 0x%04x\n", 
           rx_crc,  actual_crc);
      status = -1;       
    }
  }
  if (debug) Serial.printf("..leaving readFromRegister()\n");  
  return status;
}


uint16_t    PV_VOLTAGE    =   0x3100; //(V|100)         Solar charge controller--PV array voltage
uint16_t    PV_CURRENT    =   0X3101; //(A|100)         Solar charge controller--PV array current
uint16_t    PV_POWER_L    =   0x3102; //(W|100)         Solar charge controller--PV array power
uint16_t    PV_POWER_H    =   0x3103; //(W|100)         Solar charge controller--PV array power
uint16_t    BATT_VOLTAGE  =   0x3104; //(V|100)         Battery voltage
uint16_t    BATT_CURRENT  =   0x3105; //(A|100)         Battery charging current
uint16_t    BATT_POWER_L  =   0x3106; //(W|100)         Battery charging power
uint16_t    BATT_POWER_H  =   0x3107; //(W|100)         Battery charging power
// Updated by readLS2024B() and read by prepareTxFrame()
uint16_t    pvV = 0;
uint16_t    pvI = 0;
uint16_t    battV = 0;
uint16_t    battI = 0;
uint16_t    batteryVoltage = 0;
// Accumulated values
uint32_t    pvVLB = 0;
uint32_t    pvILB = 0;
uint32_t    battVLB = 0;
uint32_t    battILB = 0;
uint32_t    batteryVoltageLB = 0;
uint32_t    commsErrors = 0;


#define X_OFFSET 80
// Four measurements with 0.2 sec delay between each so takes about a sec
//
void readLS2024B() {
  int         rxCount;
  uint16_t    rxData[ 16 ];
  boolean     debug = false;
  if (debug) Serial.printf("Entering readLS2024B()..\n");  
  rxCount = readFromRegister( PV_VOLTAGE, 1, rxData );
  if (rxCount > 0) {
    if (debug) Serial.printf( "\nRegister 0x%04x contains 0x%04x\n", PV_VOLTAGE, rxData[0] );
    pvV = 10 * ((rxData[0] & 0x00FF) << 8) + ((rxData[0] & 0xFF00)>> 8);
    if (debug) Serial.printf( "PV_VOLTAGE = %d mV\n", pvV  );
  } else {
    Serial.printf( "\nERROR: reading register 0x%04x\n", PV_VOLTAGE  );
    pvV = 0;
    commsErrors++;     
  }
  /*
  display.clear(); 
  String row2 = String(pvV);        
  display.drawString(0, 0,  "pV (mV)  :" );
  display.drawString(X_OFFSET, 0, row2);
  display.display();
  delay(2000);
  display.clear();
  */
  delay(200);  
    
  rxCount = readFromRegister( PV_CURRENT, 1, rxData );
  if (rxCount > 0) {
    if (debug) Serial.printf( "\nRegister 0x%04x contains 0x%04x\n", PV_CURRENT, rxData[0] );
    pvI = 10 * ((rxData[0] & 0x00FF) << 8) + ((rxData[0] & 0xFF00)>> 8);
    if (debug) Serial.printf( "PV_CURRENT = %d mA\n", pvI  );
  } else {
    Serial.printf( "\nERROR: reading register 0x%04x\n", PV_CURRENT  );
    pvI = 0;
    commsErrors++;     
  }
  /*
  display.clear(); 
  row2 = String(pvI);        
  display.drawString(0, 0,  "pvI (mA) :" );
  display.drawString(X_OFFSET, 0, row2);
  display.display();
  delay(2000);
  display.clear();
  */
  delay(200);  
  
  rxCount = readFromRegister( BATT_VOLTAGE, 1, rxData );
  if (rxCount > 0) {
    if (debug) Serial.printf( "\nRegister 0x%04x contains 0x%04x\n", BATT_VOLTAGE, rxData[0] );
    battV = 10 * ((rxData[0] & 0x00FF) << 8) + ((rxData[0] & 0xFF00)>> 8);
    if (debug) Serial.printf( "BATT_VOLTAGE = %d mV\n", battV  );
  } else {
    Serial.printf( "\nERROR: reading register 0x%04x\n", BATT_VOLTAGE  );
    battV = 0;
    commsErrors++;     
  }
  /*
  display.clear(); 
  row2 = String(battV);        
  display.drawString(0, 0,  "battV (mV):" );
  display.drawString(X_OFFSET, 0, row2);
  display.display();
  delay(2000);
  display.clear();
  */
  delay(200); 
  
  rxCount = readFromRegister( BATT_CURRENT, 1, rxData );
  if (rxCount > 0) {
    if (debug) Serial.printf( "\nRegister 0x%04x contains 0x%04x\n", BATT_CURRENT, rxData[0] );
    battI = 10 * ((rxData[0] & 0x00FF) << 8) + ((rxData[0] & 0xFF00)>> 8);
    if (debug) Serial.printf( "BATT_CURRENT = %d mA\n", battI  );
  } else {
    Serial.printf( "\nERROR: reading register 0x%04x\n", BATT_CURRENT  );
    battI = 0;   
    commsErrors++; 
  }
  /*
  display.clear(); 
  row2 = String(battI);        
  display.drawString(0, 0,  "battI (mA):" );
  display.drawString(X_OFFSET, 0, row2);
  display.display();
  delay(2000);
  display.clear();
  */
  delay(200);  
   
  Serial.printf( "\n");
  if (debug) Serial.printf("..leaving readLS2024B()\n");  
}



// Each reading is accumulated for MAX_TICKS readings then divided by MAX_TICKS before being transmitted.
// 
void  takeMeasurements() {
 
  if (debug) Serial.printf("Entering takeMeasurements()..\n"); 
  
  // Update PV Charge Controller values
  readLS2024B();  
  batteryVoltage = getBatteryVoltage();
  batteryVoltageLB += (uint32_t)batteryVoltage;  
  Serial.printf("CubCell voltage : %d mV  (%d)\n", batteryVoltage, batteryVoltageLB ); 
  
  pvVLB += (uint32_t)pvV; 
  Serial.printf("PV Voltage      : %d mV  (%d)\n", pvV, pvVLB );
  pvILB += (uint32_t)pvI;    
  Serial.printf("PV Current      : %d mA  (%d)\n", pvI, pvILB ); 
  battVLB += (uint32_t)battV; 
  Serial.printf("Battery Voltage : %d mV  (%d)\n", battV, battVLB ); 
  battILB += (uint32_t)battI;  
  Serial.printf("Battery Current : %d mA  (%d)\n", battI, battILB );
  
  Serial.printf("Comms. errors : %d\n", commsErrors ); 
}
  

static void prepareTxFrame( uint8_t port )
{
  if (debug) Serial.printf("Entering prepareTxFrame()..\n"); 
  display.connect();
  vExtOn();  // Turn on display
  delay(100);
  display.init();
  display.setFont(ArialMT_Plain_16); //10, 24
  
 
  String row2 = "";
  
  pvVLB = pvVLB/MAX_TICKS;
  pvILB = pvILB/MAX_TICKS;
  battVLB = battVLB/MAX_TICKS;
  battILB = battILB/MAX_TICKS;
  batteryVoltageLB = batteryVoltageLB/MAX_TICKS;
  
  Serial.printf("PV Voltage      : %d mV\n", pvVLB  );  
  Serial.printf("PV Current      : %d mA\n", pvILB ); 
  Serial.printf("Battery Voltage : %d mV\n", battVLB ); 
  Serial.printf("Battery Current : %d mA\n", battILB );    
  Serial.printf("CubCell voltage : %d mV\n", batteryVoltageLB ); 
  Serial.printf("Comms. errors : %d\n", commsErrors ); 
  
  display.clear(); 
  row2 = String(batteryVoltageLB);        
  display.drawString(0, 0,  "LoRa (mV):" );
  display.drawString(X_OFFSET, 0, row2);
  display.display();
  delay(2000);
  display.clear();
  
  display.clear(); 
  row2 = String(commsErrors);        
  display.drawString(0, 0,  "Comm. Errs:" );
  display.drawString(X_OFFSET+10, 0, row2);
  display.display();
  delay(2000);
  display.clear();
    
  appDataSize = 12;
  appData[0] = highByte(batteryVoltageLB);
  appData[1] = lowByte(batteryVoltageLB);
  appData[2] = highByte(pvVLB);
  appData[3] = lowByte(pvVLB);
  appData[4] = highByte(pvILB);
  appData[5] = lowByte(pvILB);
  appData[6] = highByte(battVLB);
  appData[7] = lowByte(battVLB);
  appData[8] = highByte(battILB);
  appData[9] = lowByte(battILB);
  appData[10] = highByte(commsErrors);
  appData[11] = lowByte(commsErrors);

   
  for( int i=0; i<appDataSize; i++ ) Serial.printf( "0x%02x,", appData[i]);
  Serial.println("");
  vExtOff();   // Turn off display
  display.stop();
  pvVLB = 0;
  pvILB = 0;
  battVLB = 0;
  battILB = 0;
  batteryVoltageLB = 0;
  
}

void vExtOn() {
    pinMode(Vext,OUTPUT);
    digitalWrite(Vext, LOW);
}
void vExtOff() {
    pinMode(Vext,OUTPUT);
    digitalWrite(Vext, HIGH);
}
int tickCounter = 0;
void setup()
{
  boardInitMcu();
  // Serial 1 TX1, RX1 is used for programming and monitor debug output...
  Serial.begin(9600);
  while (!Serial) { ; }
  // Serial 2 TX2, RX2 is used for RS485 comms...
  Serial1.begin(115200);
  while (!Serial) { ; }
  
  Serial.printf("\n\nSketch=%s, version=%s\n", SKETCH_NAME, VERSION);

  // Initialising the UI will init the display too.
  //display(0x3c, 500000, SDA, SCL ,GEOMETRY_128_64,GPIO10); // addr, freq, sda, scl, resolution, rst
  vExtOn();
  delay(100);
    
  display.init();
  display.setFont(ArialMT_Plain_10);
  display.setTextAlignment(TEXT_ALIGN_LEFT);
  display.setFont(ArialMT_Plain_10);
  //  "Cubecell_PV_epever_monitor_slan-032_v1.16"
  String title = SKETCH_NAME;
  int index = title.indexOf("-")-4;
  display.drawString(0, 0, title.substring(1, index));
  display.drawString(0, 20, title.substring(index));
  display.drawString(0, 40, VERSION);
  display.display();
  delay(5000);
  display.clear();
  
  vExtOff(); // turn off display
  display.stop(); 
   
  pinMode(MAX485_RE, OUTPUT);
  pinMode(MAX485_DE, OUTPUT);
  digitalWrite(MAX485_RE, 0);  // Receiver Enable LOW to receive
  digitalWrite(MAX485_DE, 0);  // Driver Enable HIGH to transmit
  
  deviceState = DEVICE_STATE_INIT;
  LoRaWAN.ifskipjoin();
  tickCounter = (MAX_TICKS - 1);  // So we can test LoRaWAN transmission right after a reset
}


// Every time round the loop we take measurents
// but every 5 mins we send them over LoRaWAN
//
boolean looping = true;
void loop() {
  if (debug) Serial.printf("Entering loop()..\n");  
  tickCounter++;
  takeMeasurements(); // Takes about a second
  
  if (tickCounter == MAX_TICKS) {
    deviceState = DEVICE_STATE_INIT;
    tickCounter = 0;
    looping = true;    
    if (looping) {
      while(deviceState != DEVICE_STATE_CYCLE) {
        // Transmit the data but don't let it deep sleep
        transmitLoop(); // resets accumulated readings as well
      }
      
      int period = 30;
      Serial.printf("Delaying %d secs in state DEVICE_STATE_CYCLE to let LoRaWAN transmission take place..\n", period);
      delay(period * 1000); // Delay 10 secs for sending
    }
  }
  delay(LOOP_DELAY_MSECS); 
}
void transmitLoop()
{
  if (debug2) Serial.printf("deviceState=%d\n", deviceState);
  switch( deviceState )
  {
    case DEVICE_STATE_INIT:
    {
      if (debug2) Serial.printf("deviceState = DEVICE_STATE_INIT(%d)\n",deviceState);
      if (debug2) printDevParam();
      LoRaWAN.init(loraWanClass,loraWanRegion);
      deviceState = DEVICE_STATE_JOIN;
      break;
    }
    case DEVICE_STATE_JOIN:
    {
      if (debug2) Serial.printf("deviceState = DEVICE_STATE_JOIN(%d)\n",deviceState );      
      LoRaWAN.join();
      break;
    }
    case DEVICE_STATE_SEND:
    {
      if (debug2) Serial.printf("deviceState = DEVICE_STATE_SEND(%d)\n",deviceState );      
      prepareTxFrame( appPort );
      LoRaWAN.send();
      deviceState = DEVICE_STATE_CYCLE;
      break;
    }
    case DEVICE_STATE_CYCLE:
    {
      if (debug2) Serial.printf("deviceState = DEVICE_STATE_CYCLE(%d)\n",deviceState );      
      // Schedule next packet transmission
      txDutyCycleTime = appTxDutyCycle + randr( 0, APP_TX_DUTYCYCLE_RND );
      LoRaWAN.cycle(txDutyCycleTime);
      deviceState = DEVICE_STATE_SLEEP;
      break;
    }
    case DEVICE_STATE_SLEEP:
    {
      if (debug2) Serial.printf("deviceState = DEVICE_STATE_SLEEP(%d)\n",deviceState ); 
      looping = false;
      break;     
      //LoRaWAN.sleep();
      //break;
    }
    default:
    {
      if (debug) Serial.printf("deviceState = default(%d)\n",deviceState );      
      deviceState = DEVICE_STATE_INIT;
      break;
    }
  }
}

Below is the output from a run





(Last updated Thurs 5th May 2022 18:17)










Friday, 29 April 2022

Using a NodeMCU to monitor an eSmart3 charge controller



The wiring is as follows:


From: https://skagmo.com/page.php?p=documents/04_esmart3_review
Extra: RS485 port
The pinout on this port is as follows:
Pin Function
1 RS-485 A/+
2 RS-485 B/-
5,6 GND
7,8 +5V out

Looking at the RJ-45 plug end on (towards you) with the retaining clip at the bottom the wiring is:


Pin Number:              8               7                  6            5                 4             3              2                1    

Cable Colour:        brown        brown /       green        blue /       blue       green /        orange      orange /
                                                 white                           white                     White                          white

Function:                  +5v         +5v            GND          GND                                             B              A

           

So first let's do a baseline test using PC software and A/B wired to a RS485 to USB adaptor.


All worked OK.


tele/nodemcu_pv_charger/pv_volt

70.70







The Arduino code was based on..

Saturday, 23 April 2022

Experienting with lead acid batteries in Tola

 I have 8x 100W solar panels on my pergola and brought down my eSmart3 charge controller and 2x 12v 70Ah lead acid batteries.

I intend to set up a little mains supply system in the kitchen with a 300W inverter I have knocking around :)





Thursday, 21 April 2022

Experimenting with two 24v 100Ah LiFePO4 batteries in Kilnageer

For the last few days I've been testing my two 24v 100Ah LiFePO4 batteries.

I received them a few weeks ago but only tested BLE communications to their BMS with an Android App. I didn't try charging or discharging them.

I bought them on Jan 11, 2022 from  CERRNSS Battery Factory Store (https://www.aliexpress.com/store/912104017) on AliExpress and they cost 1,311 euros including all taxes and a mains charger. I specifically asked for them to have Wi-Fi access to their BMS. They arrived on the 1st April, 2022 so took 10 or so weeks to arrive.

But after taking them up to Mayo on Mon 18th April 2022 the testing finally began.


In Mayo I have 5x 230W solar PV panels connected to a eSmart3 charge controller which was connected to 6x lead acid leisure batteries (configured as three 24v pairs in parallel).







Wednesday, 6 April 2022

Wood delivered for pergola and PV panel stand

 The idea is the pergola will hold two vines and eventually have solar PV panels on top.



Built pergola which has three 4 foot (48" / 1220 mm) by 3 foot (36" / 920 mm) sections for solar panels. Can I get 300+ W ones to fit in there?





Friday, 1 April 2022

Finally got my LiFePO4 Batteries from China

 Happy days my batteries have arrived




Exciting unboxing them!



These are 24v 100Ah LiFePO4 batteries so combined provide 4.8 KWh (or 5 units) of electricity.

I expect only 80% can be drawn. I also have a mains charger for them.

So either I charge them at a night rate in Tola and discharge during the day to reduce my electricity bill. But Community Power haven't implemented my night rate yet.

Or take them to Mayo and see if they can supply enough current @24V to my 4KW inverter so that I can run the oven for an hour (2KWh). Something I'm yet to achieve.

I also downloaded the Android BMS app to talk over  low-power Bluetooth (BLE) to each battery's Battery Management System (BMS) and all looks good.

https://drive.google.com/drive/folders/1CXmg5qO9P9VHWR4YZBSQSR12QE1Lp9Xw


They appear to by 8 strings of 3.x v LiPo batteries.



But I want to do this monitoring using a NodeMCU and Arduino later.