mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-05 02:39:57 +02:00
Merge pull request #357 from smaresca/smaresca/CHADEMO-Shunt-Refactor
CHAdeMO - shunt refactor
This commit is contained in:
commit
43c06180eb
21 changed files with 404 additions and 752 deletions
|
@ -22,7 +22,6 @@
|
||||||
#include "src/lib/eModbus-eModbus/scripts/mbServerFCs.h"
|
#include "src/lib/eModbus-eModbus/scripts/mbServerFCs.h"
|
||||||
#include "src/lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
#include "src/lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
||||||
#include "src/lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
#include "src/lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
#include "src/lib/smaresca-SimpleISA/SimpleISA.h"
|
|
||||||
|
|
||||||
#include "src/datalayer/datalayer.h"
|
#include "src/datalayer/datalayer.h"
|
||||||
|
|
||||||
|
@ -65,10 +64,6 @@ uint16_t mbPV[MB_RTU_NUM_VALUES]; // Process variable memory
|
||||||
ModbusServerRTU MBserver(Serial2, 2000);
|
ModbusServerRTU MBserver(Serial2, 2000);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ISA_SHUNT
|
|
||||||
ISA sensor;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Common charger parameters
|
// Common charger parameters
|
||||||
volatile float charger_setpoint_HV_VDC = 0.0f;
|
volatile float charger_setpoint_HV_VDC = 0.0f;
|
||||||
volatile float charger_setpoint_HV_IDC = 0.0f;
|
volatile float charger_setpoint_HV_IDC = 0.0f;
|
||||||
|
@ -522,10 +517,6 @@ void receive_can() { // This section checks if we have a complete CAN message i
|
||||||
CAN_frame_t rx_frame;
|
CAN_frame_t rx_frame;
|
||||||
if (xQueueReceive(CAN_cfg.rx_queue, &rx_frame, 0) == pdTRUE) {
|
if (xQueueReceive(CAN_cfg.rx_queue, &rx_frame, 0) == pdTRUE) {
|
||||||
|
|
||||||
//ISA Shunt
|
|
||||||
#ifdef ISA_SHUNT
|
|
||||||
sensor.handleFrame(&rx_frame);
|
|
||||||
#endif
|
|
||||||
// Battery
|
// Battery
|
||||||
#ifndef SERIAL_LINK_RECEIVER // Only needs to see inverter
|
#ifndef SERIAL_LINK_RECEIVER // Only needs to see inverter
|
||||||
receive_can_battery(rx_frame);
|
receive_can_battery(rx_frame);
|
||||||
|
|
|
@ -54,7 +54,6 @@
|
||||||
#define WEBSERVER //Enable this line to enable WiFi, and to run the webserver. See USER_SETTINGS.cpp for the Wifi settings.
|
#define WEBSERVER //Enable this line to enable WiFi, and to run the webserver. See USER_SETTINGS.cpp for the Wifi settings.
|
||||||
//#define LOAD_SAVED_SETTINGS_ON_BOOT //Enable this line to read settings stored via the webserver on boot (overrides any battery settings set in USER_SETTINGS.cpp)
|
//#define LOAD_SAVED_SETTINGS_ON_BOOT //Enable this line to read settings stored via the webserver on boot (overrides any battery settings set in USER_SETTINGS.cpp)
|
||||||
//#define FUNCTION_TIME_MEASUREMENT // Enable this to record execution times and present them in the web UI (WARNING, raises CPU load, do not use for production)
|
//#define FUNCTION_TIME_MEASUREMENT // Enable this to record execution times and present them in the web UI (WARNING, raises CPU load, do not use for production)
|
||||||
//#define ISA_SHUNT //Enable this line to build support for ISA IVT shunts
|
|
||||||
|
|
||||||
/* MQTT options */
|
/* MQTT options */
|
||||||
// #define MQTT // Enable this line to enable MQTT
|
// #define MQTT // Enable this line to enable MQTT
|
||||||
|
|
|
@ -4,11 +4,9 @@
|
||||||
#include "../devboard/utils/events.h"
|
#include "../devboard/utils/events.h"
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
||||||
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
#ifdef ISA_SHUNT
|
|
||||||
#include "../lib/smaresca-SimpleISA/SimpleISA.h"
|
|
||||||
#endif
|
|
||||||
#include "CHADEMO-BATTERY-INTERNAL.h"
|
#include "CHADEMO-BATTERY-INTERNAL.h"
|
||||||
#include "CHADEMO-BATTERY.h"
|
#include "CHADEMO-BATTERY.h"
|
||||||
|
#include "CHADEMO-SHUNTS.h"
|
||||||
|
|
||||||
/* CHADEMO handling runs at 6.25 times the rate of most other code, so, rather than the
|
/* CHADEMO handling runs at 6.25 times the rate of most other code, so, rather than the
|
||||||
* default value of 12 (for 12 iterations of the 5s value update loop) * 5 for a 60s timeout,
|
* default value of 12 (for 12 iterations of the 5s value update loop) * 5 for a 60s timeout,
|
||||||
|
@ -25,8 +23,7 @@ static unsigned long handlerAfterMillis = 0;
|
||||||
/* Do not change code below unless you are sure what you are doing */
|
/* Do not change code below unless you are sure what you are doing */
|
||||||
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
|
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
|
||||||
static unsigned long previousMillis5000 =
|
static unsigned long previousMillis5000 =
|
||||||
0; // will store last time a 5s threshold was reached for display during debug
|
0; // will store last time a 5s threshold was reached for display during debug
|
||||||
static uint8_t errorCode = 0; //stores if we have an error code active from battery control logic
|
|
||||||
|
|
||||||
bool plug_inserted = false;
|
bool plug_inserted = false;
|
||||||
bool vehicle_can_initialized = false;
|
bool vehicle_can_initialized = false;
|
||||||
|
@ -42,10 +39,6 @@ uint8_t framecount = 0;
|
||||||
|
|
||||||
uint8_t max_discharge_current = 0; //TODO not sure on this one, but really influenced by inverter capability
|
uint8_t max_discharge_current = 0; //TODO not sure on this one, but really influenced by inverter capability
|
||||||
|
|
||||||
#ifdef ISA_SHUNT
|
|
||||||
extern ISA sensor;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool high_current_control_enabled = false; // set to true when high current control is operating
|
bool high_current_control_enabled = false; // set to true when high current control is operating
|
||||||
// if true, values from 110.1 and 110.2 should be used instead of 102.3
|
// if true, values from 110.1 and 110.2 should be used instead of 102.3
|
||||||
// and 118 should be used for evse responses
|
// and 118 should be used for evse responses
|
||||||
|
@ -134,7 +127,7 @@ void update_values_battery() {
|
||||||
datalayer.battery.status.max_discharge_power_W =
|
datalayer.battery.status.max_discharge_power_W =
|
||||||
(x200_discharge_limits.MaximumDischargeCurrent * x100_chg_lim.MaximumBatteryVoltage); //In Watts, Convert A to P
|
(x200_discharge_limits.MaximumDischargeCurrent * x100_chg_lim.MaximumBatteryVoltage); //In Watts, Convert A to P
|
||||||
|
|
||||||
datalayer.battery.status.voltage_dV = sensor.Voltage * 10;
|
datalayer.battery.status.voltage_dV = get_measured_voltage() * 10;
|
||||||
|
|
||||||
datalayer.battery.info.total_capacity_Wh =
|
datalayer.battery.info.total_capacity_Wh =
|
||||||
((x101_chg_est.RatedBatteryCapacity / 0.11) *
|
((x101_chg_est.RatedBatteryCapacity / 0.11) *
|
||||||
|
@ -340,14 +333,12 @@ inline void process_vehicle_charging_limits(CAN_frame_t rx_frame) {
|
||||||
*/
|
*/
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ISA_SHUNT
|
if (get_measured_voltage() <= x200_discharge_limits.MinimumDischargeVoltage && CHADEMO_Status > CHADEMO_NEGOTIATE) {
|
||||||
if (sensor.Voltage <= x200_discharge_limits.MinimumDischargeVoltage && CHADEMO_Status > CHADEMO_NEGOTIATE) {
|
|
||||||
#ifdef DEBUG_VIA_USB
|
#ifdef DEBUG_VIA_USB
|
||||||
Serial.println("x200 minimum discharge voltage met or exceeded, stopping.");
|
Serial.println("x200 minimum discharge voltage met or exceeded, stopping.");
|
||||||
#endif
|
#endif
|
||||||
CHADEMO_Status = CHADEMO_STOP;
|
CHADEMO_Status = CHADEMO_STOP;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Vehicle 0x201, peer to EVSE 0x209
|
/* Vehicle 0x201, peer to EVSE 0x209
|
||||||
|
@ -544,18 +535,18 @@ void update_evse_status(CAN_frame_t& f) {
|
||||||
x109_evse_state.remaining_time_1m = 60;
|
x109_evse_state.remaining_time_1m = 60;
|
||||||
|
|
||||||
} else if (EVSE_mode == CHADEMO_CHARGE) {
|
} else if (EVSE_mode == CHADEMO_CHARGE) {
|
||||||
#ifdef ISA_SENSOR
|
x109_evse_state.setpoint_HV_VDC = get_measured_voltage();
|
||||||
x109_evse_state.setpoint_HV_VDC = sensor.Voltage;
|
x109_evse_state.setpoint_HV_IDC = get_measured_current();
|
||||||
x109_evse_state.setpoint_HV_IDC = sensor.Amperes;
|
|
||||||
#else
|
/*For posterity if anyone is forced to simulate a shunt
|
||||||
//NOTE: these are supposed to be measured values, e.g., from a shunt
|
NOTE: these are supposed to be measured values, e.g., from a shunt
|
||||||
//If a sensor is not used, we are literally asserting that the measured value is exactly equivalent to the request or max charger capability
|
If a sensor is not used, we are literally asserting that the measured value is exactly equivalent to the request or max charger capability
|
||||||
//this is pretty likely to fail on most vehicles
|
this is pretty likely to fail on most vehicles
|
||||||
x109_evse_state.setpoint_HV_VDC =
|
x109_evse_state.setpoint_HV_VDC =
|
||||||
min(x102_chg_session.TargetBatteryVoltage, x108_evse_cap.available_output_voltage);
|
min(x102_chg_session.TargetBatteryVoltage, x108_evse_cap.available_output_voltage);
|
||||||
x109_evse_state.setpoint_HV_IDC =
|
x109_evse_state.setpoint_HV_IDC =
|
||||||
min(x102_chg_session.ChargingCurrentRequest, x108_evse_cap.available_output_current);
|
min(x102_chg_session.ChargingCurrentRequest, x108_evse_cap.available_output_current);
|
||||||
#endif
|
*/
|
||||||
|
|
||||||
/* The spec suggests throwing a 109.5.4 = 1 if vehicle curr request 102.3 > evse curr available 108.3,
|
/* The spec suggests throwing a 109.5.4 = 1 if vehicle curr request 102.3 > evse curr available 108.3,
|
||||||
* but realistically many chargers seem to act tolerant here and stay under limits and supply whatever they are able
|
* but realistically many chargers seem to act tolerant here and stay under limits and supply whatever they are able
|
||||||
|
@ -577,7 +568,7 @@ void update_evse_status(CAN_frame_t& f) {
|
||||||
*/
|
*/
|
||||||
if ((x102_chg_session.TargetBatteryVoltage > x108_evse_cap.available_output_voltage) ||
|
if ((x102_chg_session.TargetBatteryVoltage > x108_evse_cap.available_output_voltage) ||
|
||||||
(x100_chg_lim.MaximumBatteryVoltage > x108_evse_cap.threshold_voltage)) {
|
(x100_chg_lim.MaximumBatteryVoltage > x108_evse_cap.threshold_voltage)) {
|
||||||
//Toggl battery incompatibility flag 109.5.3
|
//Toggle battery incompatibility flag 109.5.3
|
||||||
x109_evse_state.s.status.EVSE_error = 1;
|
x109_evse_state.s.status.EVSE_error = 1;
|
||||||
x109_evse_state.s.status.battery_incompatible = 1;
|
x109_evse_state.s.status.battery_incompatible = 1;
|
||||||
x109_evse_state.s.status.ChgDischStopControl = 1;
|
x109_evse_state.s.status.ChgDischStopControl = 1;
|
||||||
|
@ -626,16 +617,15 @@ void update_evse_discharge_estimate(CAN_frame_t& f) {
|
||||||
|
|
||||||
/* x208 EVSE, peer to 0x200 Vehicle */
|
/* x208 EVSE, peer to 0x200 Vehicle */
|
||||||
void update_evse_discharge_capabilities(CAN_frame_t& f) {
|
void update_evse_discharge_capabilities(CAN_frame_t& f) {
|
||||||
#ifdef ISA_SHUNT
|
|
||||||
//present discharge current is a measured value
|
//present discharge current is a measured value
|
||||||
x208_evse_dischg_cap.present_discharge_current = 0xFF - sensor.Amperes;
|
x208_evse_dischg_cap.present_discharge_current = 0xFF - get_measured_current();
|
||||||
#else
|
|
||||||
//Present discharge current is a measured value. In the absence of
|
/* Present discharge current is a measured value. In the absence of
|
||||||
// a shunt, the evse here is quite literally lying to the vehicle. The spec
|
a shunt, the evse here is quite literally lying to the vehicle. The spec
|
||||||
// seems to suggest this is tolerated unless the current measured on the EV
|
seems to suggest this is tolerated unless the current measured on the EV
|
||||||
// side continualy exceeds the maximum discharge current by 10amps
|
side continualy exceeds the maximum discharge current by 10amps
|
||||||
x208_evse_dischg_cap.present_discharge_current = 0xFF - 6;
|
x208_evse_dischg_cap.present_discharge_current = 0xFF - 6;
|
||||||
#endif
|
*/
|
||||||
|
|
||||||
//EVSE maximum current input is partly an inverter-influenced value i.e., min(inverter, vehicle_max_discharge)
|
//EVSE maximum current input is partly an inverter-influenced value i.e., min(inverter, vehicle_max_discharge)
|
||||||
//use max_discharge_current variable if nonzero, otherwise tell the vehicle the EVSE will take everything it can give
|
//use max_discharge_current variable if nonzero, otherwise tell the vehicle the EVSE will take everything it can give
|
||||||
|
@ -764,7 +754,6 @@ void send_can_battery() {
|
||||||
*/
|
*/
|
||||||
void handle_chademo_sequence() {
|
void handle_chademo_sequence() {
|
||||||
|
|
||||||
unsigned long currentMillis = millis();
|
|
||||||
precharge_low = digitalRead(PRECHARGE_PIN) == LOW;
|
precharge_low = digitalRead(PRECHARGE_PIN) == LOW;
|
||||||
positive_high = digitalRead(POSITIVE_CONTACTOR_PIN) == HIGH;
|
positive_high = digitalRead(POSITIVE_CONTACTOR_PIN) == HIGH;
|
||||||
contactors_ready = precharge_low && positive_high;
|
contactors_ready = precharge_low && positive_high;
|
||||||
|
@ -894,7 +883,7 @@ void handle_chademo_sequence() {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
if (x102_chg_session.s.status.StatusVehicleChargingEnabled) {
|
if (x102_chg_session.s.status.StatusVehicleChargingEnabled) {
|
||||||
if (sensor.Voltage < 20) {
|
if (get_measured_voltage() < 20) {
|
||||||
|
|
||||||
digitalWrite(CHADEMO_PIN_10, HIGH);
|
digitalWrite(CHADEMO_PIN_10, HIGH);
|
||||||
evse_permission = true;
|
evse_permission = true;
|
||||||
|
@ -942,7 +931,7 @@ void handle_chademo_sequence() {
|
||||||
#ifdef DEBUG_VIA_USB
|
#ifdef DEBUG_VIA_USB
|
||||||
Serial.println("Contactors ready");
|
Serial.println("Contactors ready");
|
||||||
Serial.print("Voltage: ");
|
Serial.print("Voltage: ");
|
||||||
Serial.println(sensor.Voltage);
|
Serial.println(get_measured_voltage());
|
||||||
#endif
|
#endif
|
||||||
/* transition to POWERFLOW state if discharge compatible on both sides */
|
/* transition to POWERFLOW state if discharge compatible on both sides */
|
||||||
if (x109_evse_state.discharge_compatible && x102_chg_session.s.status.StatusVehicleDischargeCompatible &&
|
if (x109_evse_state.discharge_compatible && x102_chg_session.s.status.StatusVehicleDischargeCompatible &&
|
||||||
|
@ -980,14 +969,14 @@ void handle_chademo_sequence() {
|
||||||
//TODO flag error and do not calculate power in EVSE response?
|
//TODO flag error and do not calculate power in EVSE response?
|
||||||
// probably unnecessary as other flags will be set causing this to be caught
|
// probably unnecessary as other flags will be set causing this to be caught
|
||||||
}
|
}
|
||||||
#ifdef ISA_SHUNT
|
|
||||||
if (sensor.Voltage <= x200_discharge_limits.MinimumDischargeVoltage) {
|
if (get_measured_voltage() <= x200_discharge_limits.MinimumDischargeVoltage) {
|
||||||
#ifdef DEBUG_VIA_USB
|
#ifdef DEBUG_VIA_USB
|
||||||
Serial.println("x200 minimum discharge voltage met or exceeded, stopping.");
|
Serial.println("x200 minimum discharge voltage met or exceeded, stopping.");
|
||||||
#endif
|
#endif
|
||||||
CHADEMO_Status = CHADEMO_STOP;
|
CHADEMO_Status = CHADEMO_STOP;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
// Potentially unnecessary (set in CHADEMO_EVSE_CONTACTORS_ENABLED stanza), but just in case
|
// Potentially unnecessary (set in CHADEMO_EVSE_CONTACTORS_ENABLED stanza), but just in case
|
||||||
x109_evse_state.s.status.ChgDischStopControl = 0;
|
x109_evse_state.s.status.ChgDischStopControl = 0;
|
||||||
x109_evse_state.s.status.EVSE_status = 1;
|
x109_evse_state.s.status.EVSE_status = 1;
|
||||||
|
@ -1012,7 +1001,7 @@ void handle_chademo_sequence() {
|
||||||
* We will re-enter the handler until the amperage drops sufficiently
|
* We will re-enter the handler until the amperage drops sufficiently
|
||||||
* and then transition to CHADEMO_IDLE
|
* and then transition to CHADEMO_IDLE
|
||||||
*/
|
*/
|
||||||
if (sensor.Amperes <= 5 && sensor.Voltage <= 10) {
|
if (get_measured_current() <= 5 && get_measured_voltage() <= 10) {
|
||||||
/* welding detection ideally here */
|
/* welding detection ideally here */
|
||||||
digitalWrite(CHADEMO_PIN_10, LOW);
|
digitalWrite(CHADEMO_PIN_10, LOW);
|
||||||
digitalWrite(CHADEMO_PIN_2, LOW);
|
digitalWrite(CHADEMO_PIN_2, LOW);
|
||||||
|
|
354
Software/src/battery/CHADEMO-SHUNTS.cpp
Normal file
354
Software/src/battery/CHADEMO-SHUNTS.cpp
Normal file
|
@ -0,0 +1,354 @@
|
||||||
|
/* Portions of this file are an adaptation of the SimpleISA library, originally authored by Jack Rickard.
|
||||||
|
*
|
||||||
|
* At present, this code supports the Scale IVT Modular current/voltage sensor device.
|
||||||
|
* These devices measure current, up to three voltages, and provide temperature compensation.
|
||||||
|
* Additional sensors are planned to provide flexibility/lower BOM costs.
|
||||||
|
*
|
||||||
|
* Original license/copyright header of SimpleISA is shown below:
|
||||||
|
* This library was written by Jack Rickard of EVtv - http://www.evtv.me
|
||||||
|
* copyright 2014
|
||||||
|
* You are licensed to use this library for any purpose, commercial or private,
|
||||||
|
* without restriction.
|
||||||
|
*
|
||||||
|
* 2024 - Modified to make use of ESP32-Arduino-CAN by miwagner
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "../include.h"
|
||||||
|
#ifdef CHADEMO_BATTERY
|
||||||
|
#include "../datalayer/datalayer.h"
|
||||||
|
#include "../devboard/utils/events.h"
|
||||||
|
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
||||||
|
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
|
#include "CHADEMO-BATTERY-INTERNAL.h"
|
||||||
|
#include "CHADEMO-BATTERY.h"
|
||||||
|
#include "CHADEMO-SHUNTS.h"
|
||||||
|
|
||||||
|
/* Initial frames received from ISA shunts provide invalid during initialization */
|
||||||
|
static int framecount = 0;
|
||||||
|
|
||||||
|
/* original variables/names/types from SimpleISA. These warrant refinement */
|
||||||
|
float Amperes; // Floating point with current in Amperes
|
||||||
|
double AH; //Floating point with accumulated ampere-hours
|
||||||
|
double KW;
|
||||||
|
double KWH;
|
||||||
|
|
||||||
|
double Voltage;
|
||||||
|
double Voltage1;
|
||||||
|
double Voltage2;
|
||||||
|
double Voltage3;
|
||||||
|
double VoltageHI;
|
||||||
|
double Voltage1HI;
|
||||||
|
double Voltage2HI;
|
||||||
|
double Voltage3HI;
|
||||||
|
double VoltageLO;
|
||||||
|
double Voltage1LO;
|
||||||
|
double Voltage2LO;
|
||||||
|
double Voltage3LO;
|
||||||
|
|
||||||
|
double Temperature;
|
||||||
|
|
||||||
|
bool firstframe;
|
||||||
|
double milliamps;
|
||||||
|
long watt;
|
||||||
|
long As;
|
||||||
|
long lastAs;
|
||||||
|
long wh;
|
||||||
|
long lastWh;
|
||||||
|
|
||||||
|
/* Output command frame used to alter or initialize ISA shunt behavior
|
||||||
|
* Please note that all delay/sleep operations are solely in this section of code,
|
||||||
|
* not used during normal operation. Such delays are currently commented out.
|
||||||
|
*/
|
||||||
|
CAN_frame_t outframe = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.unknown_2 = 0,
|
||||||
|
.RTR = CAN_no_RTR,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
|
||||||
|
.MsgID = 0x411,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
|
||||||
|
uint16_t get_measured_voltage() {
|
||||||
|
return (uint16_t)Voltage;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t get_measured_current() {
|
||||||
|
return (uint16_t)Amperes;
|
||||||
|
}
|
||||||
|
|
||||||
|
//This is our CAN interrupt service routine to catch inbound frames
|
||||||
|
inline void ISA_handleFrame(CAN_frame_t* frame) {
|
||||||
|
|
||||||
|
if (frame->MsgID < 0x521 || frame->MsgID > 0x528) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
framecount++;
|
||||||
|
|
||||||
|
switch (frame->MsgID) {
|
||||||
|
case 0x511:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x521:
|
||||||
|
ISA_handle521(frame);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x522:
|
||||||
|
ISA_handle522(frame);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x523:
|
||||||
|
ISA_handle523(frame);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x524:
|
||||||
|
ISA_handle524(frame);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x525:
|
||||||
|
ISA_handle525(frame);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x526:
|
||||||
|
ISA_handle526(frame);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x527:
|
||||||
|
ISA_handle527(frame);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x528:
|
||||||
|
ISA_handle528(frame);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//handle frame for Amperes
|
||||||
|
inline void ISA_handle521(CAN_frame_t* frame) {
|
||||||
|
long current = 0;
|
||||||
|
current =
|
||||||
|
(long)((frame->data.u8[5] << 24) | (frame->data.u8[4] << 16) | (frame->data.u8[3] << 8) | (frame->data.u8[2]));
|
||||||
|
|
||||||
|
milliamps = current;
|
||||||
|
Amperes = current / 1000.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
//handle frame for Voltage
|
||||||
|
inline void ISA_handle522(CAN_frame_t* frame) {
|
||||||
|
long volt =
|
||||||
|
(long)((frame->data.u8[5] << 24) | (frame->data.u8[4] << 16) | (frame->data.u8[3] << 8) | (frame->data.u8[2]));
|
||||||
|
|
||||||
|
Voltage = volt / 1000.0f;
|
||||||
|
Voltage1 = Voltage - (Voltage2 + Voltage3);
|
||||||
|
|
||||||
|
if (framecount < 150) {
|
||||||
|
VoltageLO = Voltage;
|
||||||
|
Voltage1LO = Voltage1;
|
||||||
|
} else {
|
||||||
|
if (Voltage < VoltageLO)
|
||||||
|
VoltageLO = Voltage;
|
||||||
|
if (Voltage > VoltageHI)
|
||||||
|
VoltageHI = Voltage;
|
||||||
|
if (Voltage1 < Voltage1LO)
|
||||||
|
Voltage1LO = Voltage1;
|
||||||
|
if (Voltage1 > Voltage1HI)
|
||||||
|
Voltage1HI = Voltage1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//handle frame for Voltage 2
|
||||||
|
inline void ISA_handle523(CAN_frame_t* frame) {
|
||||||
|
long volt =
|
||||||
|
(long)((frame->data.u8[5] << 24) | (frame->data.u8[4] << 16) | (frame->data.u8[3] << 8) | (frame->data.u8[2]));
|
||||||
|
|
||||||
|
Voltage2 = volt / 1000.0f;
|
||||||
|
if (Voltage2 > 3)
|
||||||
|
Voltage2 -= Voltage3;
|
||||||
|
|
||||||
|
if (framecount < 150) {
|
||||||
|
Voltage2LO = Voltage2;
|
||||||
|
} else {
|
||||||
|
if (Voltage2 < Voltage2LO)
|
||||||
|
Voltage2LO = Voltage2;
|
||||||
|
if (Voltage2 > Voltage2HI)
|
||||||
|
Voltage2HI = Voltage2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//handle frame for Voltage3
|
||||||
|
inline void ISA_handle524(CAN_frame_t* frame) {
|
||||||
|
long volt =
|
||||||
|
(long)((frame->data.u8[5] << 24) | (frame->data.u8[4] << 16) | (frame->data.u8[3] << 8) | (frame->data.u8[2]));
|
||||||
|
|
||||||
|
Voltage3 = volt / 1000.0f;
|
||||||
|
|
||||||
|
if (framecount < 150) {
|
||||||
|
Voltage3LO = Voltage3;
|
||||||
|
} else {
|
||||||
|
if (Voltage3 < Voltage3LO && Voltage3 > 10)
|
||||||
|
Voltage3LO = Voltage3;
|
||||||
|
if (Voltage3 > Voltage3HI)
|
||||||
|
Voltage3HI = Voltage3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//handle frame for Temperature
|
||||||
|
inline void ISA_handle525(CAN_frame_t* frame) {
|
||||||
|
long temp = 0;
|
||||||
|
temp = (long)((frame->data.u8[5] << 24) | (frame->data.u8[4] << 16) | (frame->data.u8[3] << 8) | (frame->data.u8[2]));
|
||||||
|
|
||||||
|
Temperature = temp / 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
//handle frame for Kilowatts
|
||||||
|
inline void ISA_handle526(CAN_frame_t* frame) {
|
||||||
|
watt = 0;
|
||||||
|
watt = (long)((frame->data.u8[5] << 24) | (frame->data.u8[4] << 16) | (frame->data.u8[3] << 8) | (frame->data.u8[2]));
|
||||||
|
KW = watt / 1000.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
//handle frame for Ampere-Hours
|
||||||
|
inline void ISA_handle527(CAN_frame_t* frame) {
|
||||||
|
As = 0;
|
||||||
|
As = (frame->data.u8[5] << 24) | (frame->data.u8[4] << 16) | (frame->data.u8[3] << 8) | (frame->data.u8[2]);
|
||||||
|
|
||||||
|
AH += (As - lastAs) / 3600.0f;
|
||||||
|
lastAs = As;
|
||||||
|
}
|
||||||
|
|
||||||
|
//handle frame for kiloWatt-hours
|
||||||
|
inline void ISA_handle528(CAN_frame_t* frame) {
|
||||||
|
wh = (long)((frame->data.u8[5] << 24) | (frame->data.u8[4] << 16) | (frame->data.u8[3] << 8) | (frame->data.u8[2]));
|
||||||
|
KWH += (wh - lastWh) / 1000.0f;
|
||||||
|
lastWh = wh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
void ISA_initialize() {
|
||||||
|
firstframe=false;
|
||||||
|
STOP();
|
||||||
|
delay(700);
|
||||||
|
for(int i=0;i<9;i++) {
|
||||||
|
Serial.println("initialization \n");
|
||||||
|
|
||||||
|
outframe.data.u8[0]=(0x20+i);
|
||||||
|
outframe.data.u8[1]=0x42;
|
||||||
|
outframe.data.u8[2]=0x02;
|
||||||
|
outframe.data.u8[3]=(0x60+(i*18));
|
||||||
|
outframe.data.u8[4]=0x00;
|
||||||
|
outframe.data.u8[5]=0x00;
|
||||||
|
outframe.data.u8[6]=0x00;
|
||||||
|
outframe.data.u8[7]=0x00;
|
||||||
|
|
||||||
|
ESP32Can.CANWriteFrame(&outframe);
|
||||||
|
|
||||||
|
delay(500);
|
||||||
|
|
||||||
|
sendSTORE();
|
||||||
|
delay(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
START();
|
||||||
|
delay(500);
|
||||||
|
lastAs=As;
|
||||||
|
lastWh=wh;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ISA_STOP() {
|
||||||
|
outframe.data.u8[0]=0x34;
|
||||||
|
outframe.data.u8[1]=0x00;
|
||||||
|
outframe.data.u8[2]=0x01;
|
||||||
|
outframe.data.u8[3]=0x00;
|
||||||
|
outframe.data.u8[4]=0x00;
|
||||||
|
outframe.data.u8[5]=0x00;
|
||||||
|
outframe.data.u8[6]=0x00;
|
||||||
|
outframe.data.u8[7]=0x00;
|
||||||
|
ESP32Can.CANWriteFrame(&outframe);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ISA_sendSTORE() {
|
||||||
|
outframe.data.u8[0]=0x32;
|
||||||
|
outframe.data.u8[1]=0x00;
|
||||||
|
outframe.data.u8[2]=0x00;
|
||||||
|
outframe.data.u8[3]=0x00;
|
||||||
|
outframe.data.u8[4]=0x00;
|
||||||
|
outframe.data.u8[5]=0x00;
|
||||||
|
outframe.data.u8[6]=0x00;
|
||||||
|
outframe.data.u8[7]=0x00;
|
||||||
|
ESP32Can.CANWriteFrame(&outframe);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ISA_START() {
|
||||||
|
outframe.data.u8[0]=0x34;
|
||||||
|
outframe.data.u8[1]=0x01;
|
||||||
|
outframe.data.u8[2]=0x01;
|
||||||
|
outframe.data.u8[3]=0x00;
|
||||||
|
outframe.data.u8[4]=0x00;
|
||||||
|
outframe.data.u8[5]=0x00;
|
||||||
|
outframe.data.u8[6]=0x00;
|
||||||
|
outframe.data.u8[7]=0x00;
|
||||||
|
ESP32Can.CANWriteFrame(&outframe);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ISA_RESTART() {
|
||||||
|
//Has the effect of zeroing AH and KWH
|
||||||
|
outframe.data.u8[0]=0x3F;
|
||||||
|
outframe.data.u8[1]=0x00;
|
||||||
|
outframe.data.u8[2]=0x00;
|
||||||
|
outframe.data.u8[3]=0x00;
|
||||||
|
outframe.data.u8[4]=0x00;
|
||||||
|
outframe.data.u8[5]=0x00;
|
||||||
|
outframe.data.u8[6]=0x00;
|
||||||
|
outframe.data.u8[7]=0x00;
|
||||||
|
ESP32Can.CANWriteFrame(&outframe);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ISA_deFAULT() {
|
||||||
|
//Returns module to original defaults
|
||||||
|
outframe.data.u8[0]=0x3D;
|
||||||
|
outframe.data.u8[1]=0x00;
|
||||||
|
outframe.data.u8[2]=0x00;
|
||||||
|
outframe.data.u8[3]=0x00;
|
||||||
|
outframe.data.u8[4]=0x00;
|
||||||
|
outframe.data.u8[5]=0x00;
|
||||||
|
outframe.data.u8[6]=0x00;
|
||||||
|
outframe.data.u8[7]=0x00;
|
||||||
|
ESP32Can.CANWriteFrame(&outframe);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ISA_initCurrent() {
|
||||||
|
STOP();
|
||||||
|
delay(500);
|
||||||
|
|
||||||
|
Serial.println("initialization \n");
|
||||||
|
|
||||||
|
outframe.data.u8[0]=0x21;
|
||||||
|
outframe.data.u8[1]=0x42;
|
||||||
|
outframe.data.u8[2]=0x01;
|
||||||
|
outframe.data.u8[3]=0x61;
|
||||||
|
outframe.data.u8[4]=0x00;
|
||||||
|
outframe.data.u8[5]=0x00;
|
||||||
|
outframe.data.u8[6]=0x00;
|
||||||
|
outframe.data.u8[7]=0x00;
|
||||||
|
|
||||||
|
ESP32Can.CANWriteFrame(&outframe);
|
||||||
|
|
||||||
|
delay(500);
|
||||||
|
|
||||||
|
sendSTORE();
|
||||||
|
delay(500);
|
||||||
|
|
||||||
|
START();
|
||||||
|
delay(500);
|
||||||
|
lastAs=As;
|
||||||
|
lastWh=wh;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#endif
|
16
Software/src/battery/CHADEMO-SHUNTS.h
Normal file
16
Software/src/battery/CHADEMO-SHUNTS.h
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#ifndef CHADEMO_SHUNTS_H
|
||||||
|
#define CHADEMO_SHUNTS_H
|
||||||
|
|
||||||
|
uint16_t get_measured_voltage();
|
||||||
|
uint16_t get_measured_current();
|
||||||
|
inline void ISA_handler(CAN_frame_t* frame);
|
||||||
|
inline void ISA_handle521(CAN_frame_t* frame);
|
||||||
|
inline void ISA_handle522(CAN_frame_t* frame);
|
||||||
|
inline void ISA_handle523(CAN_frame_t* frame);
|
||||||
|
inline void ISA_handle524(CAN_frame_t* frame);
|
||||||
|
inline void ISA_handle525(CAN_frame_t* frame);
|
||||||
|
inline void ISA_handle526(CAN_frame_t* frame);
|
||||||
|
inline void ISA_handle527(CAN_frame_t* frame);
|
||||||
|
inline void ISA_handle528(CAN_frame_t* frame);
|
||||||
|
|
||||||
|
#endif
|
|
@ -103,6 +103,13 @@ typedef struct {
|
||||||
DATALAYER_BATTERY_SETTINGS_TYPE settings;
|
DATALAYER_BATTERY_SETTINGS_TYPE settings;
|
||||||
} DATALAYER_BATTERY_TYPE;
|
} DATALAYER_BATTERY_TYPE;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
/** measured voltage in deciVolts. 4200 = 420.0 V */
|
||||||
|
uint16_t measured_voltage_dV = 0;
|
||||||
|
/** measured amperage in deciAmperes. 300 = 30.0 A */
|
||||||
|
uint16_t measured_amperage_dA = 0;
|
||||||
|
} DATALAYER_SHUNT_TYPE;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
// TODO
|
// TODO
|
||||||
} DATALAYER_SYSTEM_INFO_TYPE;
|
} DATALAYER_SYSTEM_INFO_TYPE;
|
||||||
|
@ -170,6 +177,7 @@ typedef struct {
|
||||||
class DataLayer {
|
class DataLayer {
|
||||||
public:
|
public:
|
||||||
DATALAYER_BATTERY_TYPE battery;
|
DATALAYER_BATTERY_TYPE battery;
|
||||||
|
DATALAYER_SHUNT_TYPE shunt;
|
||||||
DATALAYER_SYSTEM_TYPE system;
|
DATALAYER_SYSTEM_TYPE system;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,4 +0,0 @@
|
||||||
http://www.digikey.com/short/3c2wwr
|
|
||||||
|
|
||||||
This digikey shopping cart contains all the connectors and pins
|
|
||||||
for the ISA IVT-1K-U3-TOI-CAN2-12 Current Sensor
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,9 +0,0 @@
|
||||||
# SimpleISA
|
|
||||||
Simple library for IVT shunts.
|
|
||||||
Based on the EVTV library of 2016, revised for use with CHAdeMO.
|
|
||||||
Originally intended to integrate with Arduino Due.
|
|
||||||
Adapted for ESP32 and ESP32-Arduino-CAN for use in the Battery-Emulator project https://github.com/dalathegreat/Battery-Emulator
|
|
||||||
hosted at https://github.com/smaresca/SimpleISA-ESP32-Arduino-CAN
|
|
||||||
|
|
||||||
Derived from https://github.com/isaac96/simpleISA/ and https://github.com/damienmaguire/SimpleISA/
|
|
||||||
|
|
|
@ -1,396 +0,0 @@
|
||||||
/* This library supports ISA Scale IVT Modular current/voltage sensor device. These devices measure current, up to three voltages, and provide temperature compensation.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
This library was written by Jack Rickard of EVtv - http://www.evtv.me
|
|
||||||
copyright 2014
|
|
||||||
You are licensed to use this library for any purpose, commercial or private,
|
|
||||||
without restriction.
|
|
||||||
|
|
||||||
2024 - Modified to make use of ESP32-Arduino-CAN by miwagner
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "SimpleISA.h"
|
|
||||||
|
|
||||||
template<class T> inline Print &operator <<(Print &obj, T arg) { obj.print(arg); return obj; }
|
|
||||||
|
|
||||||
|
|
||||||
ISA::ISA() // Define the constructor.
|
|
||||||
{
|
|
||||||
|
|
||||||
timestamp = millis();
|
|
||||||
debug=false;
|
|
||||||
debug2=false;
|
|
||||||
framecount=0;
|
|
||||||
firstframe=true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ISA::~ISA() //Define destructor
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ISA::begin(int Port, int speed)
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void ISA::handleFrame(CAN_frame_t *frame)
|
|
||||||
|
|
||||||
//This is our CAN interrupt service routine to catch inbound frames
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
switch (frame->MsgID)
|
|
||||||
{
|
|
||||||
case 0x511:
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x521:
|
|
||||||
handle521(frame);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x522:
|
|
||||||
handle522(frame);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x523:
|
|
||||||
handle523(frame);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x524:
|
|
||||||
handle524(frame);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x525:
|
|
||||||
handle525(frame);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x526:
|
|
||||||
handle526(frame);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x527:
|
|
||||||
handle527(frame);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x528:
|
|
||||||
handle528(frame);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(debug)printCAN(frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ISA::handle521(CAN_frame_t *frame) //AMperes
|
|
||||||
|
|
||||||
{
|
|
||||||
framecount++;
|
|
||||||
long current=0;
|
|
||||||
current = (long)((frame->data.u8[5] << 24) | (frame->data.u8[4] << 16) | (frame->data.u8[3] << 8) | (frame->data.u8[2]));
|
|
||||||
|
|
||||||
milliamps=current;
|
|
||||||
Amperes=current/1000.0f;
|
|
||||||
|
|
||||||
if(debug2)Serial<<"Current: "<<Amperes<<" amperes "<<milliamps<<" ma frames:"<<framecount<<"\n";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void ISA::handle522(CAN_frame_t *frame) //Voltage
|
|
||||||
|
|
||||||
{
|
|
||||||
framecount++;
|
|
||||||
long volt=0;
|
|
||||||
volt = (long)((frame->data.u8[5] << 24) | (frame->data.u8[4] << 16) | (frame->data.u8[3] << 8) | (frame->data.u8[2]));
|
|
||||||
|
|
||||||
Voltage=volt/1000.0f;
|
|
||||||
Voltage1=Voltage-(Voltage2+Voltage3);
|
|
||||||
if(framecount<150)
|
|
||||||
{
|
|
||||||
VoltageLO=Voltage;
|
|
||||||
Voltage1LO=Voltage1;
|
|
||||||
}
|
|
||||||
if(Voltage<VoltageLO && framecount>150)VoltageLO=Voltage;
|
|
||||||
if(Voltage>VoltageHI && framecount>150)VoltageHI=Voltage;
|
|
||||||
if(Voltage1<Voltage1LO && framecount>150)Voltage1LO=Voltage1;
|
|
||||||
if(Voltage1>Voltage1HI && framecount>150)Voltage1HI=Voltage1;
|
|
||||||
|
|
||||||
if(debug2)Serial<<"Voltage: "<<Voltage<<" vdc Voltage 1: "<<Voltage1<<" vdc "<<volt<<" mVdc frames:"<<framecount<<"\n";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void ISA::handle523(CAN_frame_t *frame) //Voltage2
|
|
||||||
|
|
||||||
{
|
|
||||||
framecount++;
|
|
||||||
long volt=0;
|
|
||||||
volt = (long)((frame->data.u8[5] << 24) | (frame->data.u8[4] << 16) | (frame->data.u8[3] << 8) | (frame->data.u8[2]));
|
|
||||||
|
|
||||||
Voltage2=volt/1000.0f;
|
|
||||||
if(Voltage2>3)Voltage2-=Voltage3;
|
|
||||||
if(framecount<150)Voltage2LO=Voltage2;
|
|
||||||
if(Voltage2<Voltage2LO && framecount>150)Voltage2LO=Voltage2;
|
|
||||||
if(Voltage2>Voltage2HI&& framecount>150)Voltage2HI=Voltage2;
|
|
||||||
|
|
||||||
|
|
||||||
if(debug2)Serial<<"Voltage: "<<Voltage<<" vdc Voltage 2: "<<Voltage2<<" vdc "<<volt<<" mVdc frames:"<<framecount<<"\n";
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void ISA::handle524(CAN_frame_t *frame) //Voltage3
|
|
||||||
|
|
||||||
{
|
|
||||||
framecount++;
|
|
||||||
long volt=0;
|
|
||||||
volt = (long)((frame->data.u8[5] << 24) | (frame->data.u8[4] << 16) | (frame->data.u8[3] << 8) | (frame->data.u8[2]));
|
|
||||||
|
|
||||||
Voltage3=volt/1000.0f;
|
|
||||||
if(framecount<150)Voltage3LO=Voltage3;
|
|
||||||
if(Voltage3<Voltage3LO && framecount>150 && Voltage3>10)Voltage3LO=Voltage3;
|
|
||||||
if(Voltage3>Voltage3HI && framecount>150)Voltage3HI=Voltage3;
|
|
||||||
|
|
||||||
if(debug2)Serial<<"Voltage: "<<Voltage<<" vdc Voltage 3: "<<Voltage3<<" vdc "<<volt<<" mVdc frames:"<<framecount<<"\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
void ISA::handle525(CAN_frame_t *frame) //Temperature
|
|
||||||
|
|
||||||
{
|
|
||||||
framecount++;
|
|
||||||
long temp=0;
|
|
||||||
temp = (long)((frame->data.u8[5] << 24) | (frame->data.u8[4] << 16) | (frame->data.u8[3] << 8) | (frame->data.u8[2]));
|
|
||||||
|
|
||||||
Temperature=temp/10;
|
|
||||||
|
|
||||||
if(debug2)Serial<<"Temperature: "<<Temperature<<" C frames:"<<framecount<<"\n";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void ISA::handle526(CAN_frame_t *frame) //Kilowatts
|
|
||||||
|
|
||||||
{
|
|
||||||
framecount++;
|
|
||||||
watt=0;
|
|
||||||
watt = (long)((frame->data.u8[5] << 24) | (frame->data.u8[4] << 16) | (frame->data.u8[3] << 8) | (frame->data.u8[2]));
|
|
||||||
|
|
||||||
KW=watt/1000.0f;
|
|
||||||
|
|
||||||
if(debug2)Serial<<"Power: "<<watt<<" Watts "<<KW<<" kW frames:"<<framecount<<"\n";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void ISA::handle527(CAN_frame_t *frame) //Ampere-Hours
|
|
||||||
|
|
||||||
{
|
|
||||||
framecount++;
|
|
||||||
As=0;
|
|
||||||
As = (frame->data.u8[5] << 24) | (frame->data.u8[4] << 16) | (frame->data.u8[3] << 8) | (frame->data.u8[2]);
|
|
||||||
|
|
||||||
AH+=(As-lastAs)/3600.0f;
|
|
||||||
lastAs=As;
|
|
||||||
|
|
||||||
|
|
||||||
if(debug2)Serial<<"Amphours: "<<AH<<" Ampseconds: "<<As<<" frames:"<<framecount<<"\n";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void ISA::handle528(CAN_frame_t *frame) //kiloWatt-hours
|
|
||||||
|
|
||||||
{
|
|
||||||
framecount++;
|
|
||||||
|
|
||||||
wh = (long)((frame->data.u8[5] << 24) | (frame->data.u8[4] << 16) | (frame->data.u8[3] << 8) | (frame->data.u8[2]));
|
|
||||||
KWH+=(wh-lastWh)/1000.0f;
|
|
||||||
lastWh=wh;
|
|
||||||
if(debug2)Serial<<"KiloWattHours: "<<KWH<<" Watt Hours: "<<wh<<" frames:"<<framecount<<"\n";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void ISA::printCAN(CAN_frame_t *frame)
|
|
||||||
{
|
|
||||||
|
|
||||||
//This routine simply prints a timestamp and the contents of the
|
|
||||||
//incoming CAN message
|
|
||||||
|
|
||||||
milliseconds = (int) (millis()/1) %1000 ;
|
|
||||||
seconds = (int) (millis() / 1000) % 60 ;
|
|
||||||
minutes = (int) ((millis() / (1000*60)) % 60);
|
|
||||||
hours = (int) ((millis() / (1000*60*60)) % 24);
|
|
||||||
sprintf(buffer,"%02d:%02d:%02d.%03d", hours, minutes, seconds, milliseconds);
|
|
||||||
Serial<<buffer<<" ";
|
|
||||||
sprintf(bigbuffer,"%02X %02X %02X %02X %02X %02X %02X %02X %02X",
|
|
||||||
frame->MsgID, frame->data.u8[0],frame->data.u8[1],frame->data.u8[2],
|
|
||||||
frame->data.u8[3],frame->data.u8[4],frame->data.u8[5],frame->data.u8[6],frame->data.u8[7],0);
|
|
||||||
Serial<<"Rcvd ISA frame: 0x"<<bigbuffer<<"\n";
|
|
||||||
|
|
||||||
}
|
|
||||||
void ISA::initialize()
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
firstframe=false;
|
|
||||||
STOP();
|
|
||||||
delay(700);
|
|
||||||
for(int i=0;i<9;i++)
|
|
||||||
{
|
|
||||||
|
|
||||||
Serial.println("initialization \n");
|
|
||||||
|
|
||||||
outframe.data.u8[0]=(0x20+i);
|
|
||||||
outframe.data.u8[1]=0x42;
|
|
||||||
outframe.data.u8[2]=0x02;
|
|
||||||
outframe.data.u8[3]=(0x60+(i*18));
|
|
||||||
outframe.data.u8[4]=0x00;
|
|
||||||
outframe.data.u8[5]=0x00;
|
|
||||||
outframe.data.u8[6]=0x00;
|
|
||||||
outframe.data.u8[7]=0x00;
|
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&outframe);
|
|
||||||
|
|
||||||
if(debug)printCAN(&outframe);
|
|
||||||
delay(500);
|
|
||||||
|
|
||||||
sendSTORE();
|
|
||||||
delay(500);
|
|
||||||
}
|
|
||||||
// delay(500);
|
|
||||||
START();
|
|
||||||
delay(500);
|
|
||||||
lastAs=As;
|
|
||||||
lastWh=wh;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void ISA::STOP()
|
|
||||||
{
|
|
||||||
|
|
||||||
//SEND STOP///////
|
|
||||||
|
|
||||||
outframe.data.u8[0]=0x34;
|
|
||||||
outframe.data.u8[1]=0x00;
|
|
||||||
outframe.data.u8[2]=0x01;
|
|
||||||
outframe.data.u8[3]=0x00;
|
|
||||||
outframe.data.u8[4]=0x00;
|
|
||||||
outframe.data.u8[5]=0x00;
|
|
||||||
outframe.data.u8[6]=0x00;
|
|
||||||
outframe.data.u8[7]=0x00;
|
|
||||||
ESP32Can.CANWriteFrame(&outframe);
|
|
||||||
|
|
||||||
if(debug) {printCAN(&outframe);} //If the debug variable is set, show our transmitted frame
|
|
||||||
}
|
|
||||||
void ISA::sendSTORE()
|
|
||||||
{
|
|
||||||
|
|
||||||
//SEND STORE///////
|
|
||||||
|
|
||||||
outframe.data.u8[0]=0x32;
|
|
||||||
outframe.data.u8[1]=0x00;
|
|
||||||
outframe.data.u8[2]=0x00;
|
|
||||||
outframe.data.u8[3]=0x00;
|
|
||||||
outframe.data.u8[4]=0x00;
|
|
||||||
outframe.data.u8[5]=0x00;
|
|
||||||
outframe.data.u8[6]=0x00;
|
|
||||||
outframe.data.u8[7]=0x00;
|
|
||||||
ESP32Can.CANWriteFrame(&outframe);
|
|
||||||
|
|
||||||
if(debug)printCAN(&outframe); //If the debug variable is set, show our transmitted frame
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void ISA::START()
|
|
||||||
{
|
|
||||||
|
|
||||||
//SEND START///////
|
|
||||||
|
|
||||||
outframe.data.u8[0]=0x34;
|
|
||||||
outframe.data.u8[1]=0x01;
|
|
||||||
outframe.data.u8[2]=0x01;
|
|
||||||
outframe.data.u8[3]=0x00;
|
|
||||||
outframe.data.u8[4]=0x00;
|
|
||||||
outframe.data.u8[5]=0x00;
|
|
||||||
outframe.data.u8[6]=0x00;
|
|
||||||
outframe.data.u8[7]=0x00;
|
|
||||||
ESP32Can.CANWriteFrame(&outframe);
|
|
||||||
|
|
||||||
if(debug)printCAN(&outframe); //If the debug variable is set, show our transmitted frame
|
|
||||||
}
|
|
||||||
|
|
||||||
void ISA::RESTART()
|
|
||||||
{
|
|
||||||
//Has the effect of zeroing AH and KWH
|
|
||||||
|
|
||||||
outframe.data.u8[0]=0x3F;
|
|
||||||
outframe.data.u8[1]=0x00;
|
|
||||||
outframe.data.u8[2]=0x00;
|
|
||||||
outframe.data.u8[3]=0x00;
|
|
||||||
outframe.data.u8[4]=0x00;
|
|
||||||
outframe.data.u8[5]=0x00;
|
|
||||||
outframe.data.u8[6]=0x00;
|
|
||||||
outframe.data.u8[7]=0x00;
|
|
||||||
ESP32Can.CANWriteFrame(&outframe);
|
|
||||||
|
|
||||||
if(debug)printCAN(&outframe); //If the debug variable is set, show our transmitted frame
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void ISA::deFAULT()
|
|
||||||
{
|
|
||||||
//Returns module to original defaults
|
|
||||||
|
|
||||||
outframe.data.u8[0]=0x3D;
|
|
||||||
outframe.data.u8[1]=0x00;
|
|
||||||
outframe.data.u8[2]=0x00;
|
|
||||||
outframe.data.u8[3]=0x00;
|
|
||||||
outframe.data.u8[4]=0x00;
|
|
||||||
outframe.data.u8[5]=0x00;
|
|
||||||
outframe.data.u8[6]=0x00;
|
|
||||||
outframe.data.u8[7]=0x00;
|
|
||||||
ESP32Can.CANWriteFrame(&outframe);
|
|
||||||
|
|
||||||
if(debug)printCAN(&outframe); //If the debug variable is set, show our transmitted frame
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void ISA::initCurrent()
|
|
||||||
{
|
|
||||||
STOP();
|
|
||||||
delay(500);
|
|
||||||
|
|
||||||
|
|
||||||
Serial.println("initialization \n");
|
|
||||||
|
|
||||||
outframe.data.u8[0]=(0x21);
|
|
||||||
outframe.data.u8[1]=0x42;
|
|
||||||
outframe.data.u8[2]=0x01;
|
|
||||||
outframe.data.u8[3]=(0x61);
|
|
||||||
outframe.data.u8[4]=0x00;
|
|
||||||
outframe.data.u8[5]=0x00;
|
|
||||||
outframe.data.u8[6]=0x00;
|
|
||||||
outframe.data.u8[7]=0x00;
|
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&outframe);
|
|
||||||
|
|
||||||
if(debug)printCAN(&outframe);
|
|
||||||
delay(500);
|
|
||||||
|
|
||||||
sendSTORE();
|
|
||||||
delay(500);
|
|
||||||
|
|
||||||
// delay(500);
|
|
||||||
START();
|
|
||||||
delay(500);
|
|
||||||
lastAs=As;
|
|
||||||
lastWh=wh;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,108 +0,0 @@
|
||||||
#ifndef SimpleISA_h
|
|
||||||
#define SimpleISA_h
|
|
||||||
|
|
||||||
/* This library supports the ISA Scale IVT Modular current/voltage sensor device. These devices measure current, up to three voltages, and provide temperature compensation.
|
|
||||||
|
|
||||||
This library was written by Jack Rickard of EVtv - http://www.evtv.me copyright 2016
|
|
||||||
You are licensed to use this library for any purpose, commercial or private,
|
|
||||||
without restriction.
|
|
||||||
|
|
||||||
Note for posterity: IVT-MOD has X1 pinout: vcc gnd CAN-L CAN-H
|
|
||||||
IVT-S has X1 pinout: vcc CAN-L CAN-H GND
|
|
||||||
|
|
||||||
*/
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include "../miwagner-ESP32-Arduino-CAN/CAN_config.h"
|
|
||||||
#include "../miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
|
||||||
|
|
||||||
class ISA
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
|
||||||
ISA();
|
|
||||||
~ISA();
|
|
||||||
void initialize();
|
|
||||||
void begin(int Port, int speed);
|
|
||||||
void initCurrent();
|
|
||||||
void sendSTORE();
|
|
||||||
void STOP();
|
|
||||||
void START();
|
|
||||||
void RESTART();
|
|
||||||
void deFAULT();
|
|
||||||
|
|
||||||
|
|
||||||
float Amperes; // Floating point with current in Amperes
|
|
||||||
double AH; //Floating point with accumulated ampere-hours
|
|
||||||
double KW;
|
|
||||||
double KWH;
|
|
||||||
|
|
||||||
|
|
||||||
double Voltage;
|
|
||||||
double Voltage1;
|
|
||||||
double Voltage2;
|
|
||||||
double Voltage3;
|
|
||||||
double VoltageHI;
|
|
||||||
double Voltage1HI;
|
|
||||||
double Voltage2HI;
|
|
||||||
double Voltage3HI;
|
|
||||||
double VoltageLO;
|
|
||||||
double Voltage1LO;
|
|
||||||
double Voltage2LO;
|
|
||||||
double Voltage3LO;
|
|
||||||
|
|
||||||
double Temperature;
|
|
||||||
|
|
||||||
bool debug;
|
|
||||||
bool debug2;
|
|
||||||
bool firstframe;
|
|
||||||
int framecount;
|
|
||||||
unsigned long timestamp;
|
|
||||||
double milliamps;
|
|
||||||
long watt;
|
|
||||||
long As;
|
|
||||||
long lastAs;
|
|
||||||
long wh;
|
|
||||||
long lastWh;
|
|
||||||
void handleFrame(CAN_frame_t *frame); // CAN handler
|
|
||||||
uint8_t page;
|
|
||||||
|
|
||||||
private:
|
|
||||||
CAN_frame_t frame;
|
|
||||||
unsigned long elapsedtime;
|
|
||||||
double ampseconds;
|
|
||||||
int milliseconds ;
|
|
||||||
int seconds;
|
|
||||||
int minutes;
|
|
||||||
int hours;
|
|
||||||
char buffer[9];
|
|
||||||
char bigbuffer[90];
|
|
||||||
uint32_t inbox;
|
|
||||||
CAN_frame_t outframe = {
|
|
||||||
.FIR = {.B = {
|
|
||||||
.DLC = 8,
|
|
||||||
.unknown_2 = 0,
|
|
||||||
.RTR = CAN_no_RTR,
|
|
||||||
.FF = CAN_frame_std,
|
|
||||||
}},
|
|
||||||
.MsgID = 0x411,
|
|
||||||
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
|
||||||
|
|
||||||
|
|
||||||
void printCAN(CAN_frame_t *frame);
|
|
||||||
void handle521(CAN_frame_t *frame);
|
|
||||||
void handle522(CAN_frame_t *frame);
|
|
||||||
void handle523(CAN_frame_t *frame);
|
|
||||||
void handle524(CAN_frame_t *frame);
|
|
||||||
void handle525(CAN_frame_t *frame);
|
|
||||||
void handle526(CAN_frame_t *frame);
|
|
||||||
void handle527(CAN_frame_t *frame);
|
|
||||||
void handle528(CAN_frame_t *frame);
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* SimpleISA_h */
|
|
|
@ -1,188 +0,0 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include <due_can.h>
|
|
||||||
#include "variant.h"
|
|
||||||
#include <SimpleISA.h>
|
|
||||||
|
|
||||||
#define Serial SerialUSB //Use native port
|
|
||||||
template<class T> inline Print &operator <<(Print &obj, T arg) { obj.print(arg); return obj; } //Allow streaming
|
|
||||||
|
|
||||||
float Version=2.00;
|
|
||||||
uint16_t loopcount=0;
|
|
||||||
unsigned long startime=0;
|
|
||||||
unsigned long elapsedtime=0;
|
|
||||||
uint port=0;
|
|
||||||
uint16_t datarate=500;
|
|
||||||
|
|
||||||
ISA Sensor; //Instantiate ISA Module Sensor object to measure current and voltage
|
|
||||||
|
|
||||||
|
|
||||||
void setup()
|
|
||||||
{
|
|
||||||
Serial.begin(115200);
|
|
||||||
Sensor.begin(port,datarate); //Start ISA object on CAN 0 at 500 kbps
|
|
||||||
|
|
||||||
Serial<<"\nISA Scale Startup Successful \n";
|
|
||||||
|
|
||||||
printMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop()
|
|
||||||
{
|
|
||||||
if(loopcount++==40000)
|
|
||||||
{
|
|
||||||
printStatus();
|
|
||||||
loopcount-0;
|
|
||||||
}
|
|
||||||
checkforinput(); //Check keyboard for user input
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void printStatus()
|
|
||||||
{
|
|
||||||
char buffer[40];
|
|
||||||
//printimestamp();
|
|
||||||
|
|
||||||
sprintf(buffer,"%4.2f",Sensor.Voltage);
|
|
||||||
Serial<<"Volt:"<<buffer<<"v ";
|
|
||||||
sprintf(buffer,"%4.2f",Sensor.Voltage1);
|
|
||||||
Serial<<"V1:"<<buffer<<"v ";
|
|
||||||
sprintf(buffer,"%4.2f",Sensor.Voltage2);
|
|
||||||
Serial<<"V2:"<<buffer<<"v ";
|
|
||||||
sprintf(buffer,"%4.2f",Sensor.Voltage3);
|
|
||||||
Serial<<"V3:"<<buffer<<"v ";
|
|
||||||
|
|
||||||
sprintf(buffer,"%4.3f",Sensor.Amperes);
|
|
||||||
Serial<<"Amps:"<<buffer<<"A ";
|
|
||||||
|
|
||||||
sprintf(buffer,"%4.3f",Sensor.KW);
|
|
||||||
Serial<<buffer<<"kW ";
|
|
||||||
|
|
||||||
sprintf(buffer,"%4.3f",Sensor.AH);
|
|
||||||
Serial<<buffer<<"Ah ";
|
|
||||||
|
|
||||||
sprintf(buffer,"%4.3f",Sensor.KWH);
|
|
||||||
Serial<<buffer<<"kWh";
|
|
||||||
|
|
||||||
sprintf(buffer,"%4.0f",Sensor.Temperature);
|
|
||||||
Serial<<buffer<<"C ";
|
|
||||||
|
|
||||||
Serial<<"Frame:"<<Sensor.framecount<<" \n";
|
|
||||||
}
|
|
||||||
|
|
||||||
void printimestamp()
|
|
||||||
{
|
|
||||||
//Prints a timestamp to the serial port
|
|
||||||
elapsedtime=millis() - startime;
|
|
||||||
|
|
||||||
int milliseconds = (elapsedtime/1) %1000 ;
|
|
||||||
int seconds = (elapsedtime / 1000) % 60 ;
|
|
||||||
int minutes = ((elapsedtime / (1000*60)) % 60);
|
|
||||||
int hours = ((elapsedtime / (1000*60*60)) % 24);
|
|
||||||
char buffer[19];
|
|
||||||
sprintf(buffer,"%02d:%02d:%02d.%03d", hours, minutes, seconds, milliseconds);
|
|
||||||
Serial<<buffer<<" ";
|
|
||||||
}
|
|
||||||
|
|
||||||
void printMenu()
|
|
||||||
{
|
|
||||||
Serial<<"\f\n=========== ISA Scale Sample Program Version "<<Version<<" ==============\n************ List of Available Commands ************\n\n";
|
|
||||||
Serial<<" ? - Print this menu\n ";
|
|
||||||
Serial<<" d - toggles Debug off and on to print recieved CAN data traffic\n";
|
|
||||||
Serial<<" D - toggles Debug2 off and on to print derived values\n";
|
|
||||||
Serial<<" f - zero frame count\n ";
|
|
||||||
Serial<<" i - initialize new sensor\n ";
|
|
||||||
Serial<<" p - Select new CAN port\n ";
|
|
||||||
Serial<<" r - Set new datarate\n ";
|
|
||||||
Serial<<" z - zero ampere-hours\n ";
|
|
||||||
|
|
||||||
Serial<<"**************************************************************\n==============================================================\n\n";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void checkforinput()
|
|
||||||
{
|
|
||||||
//Checks for keyboard input from Native port
|
|
||||||
if (Serial.available())
|
|
||||||
{
|
|
||||||
int inByte = Serial.read();
|
|
||||||
switch (inByte)
|
|
||||||
{
|
|
||||||
case 'z': //Zeroes ampere-hours
|
|
||||||
Sensor.KWH=0;
|
|
||||||
Sensor.AH=0;
|
|
||||||
Sensor.RESTART();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'p':
|
|
||||||
getPort();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'r':
|
|
||||||
getRate();
|
|
||||||
break;
|
|
||||||
|
|
||||||
|
|
||||||
case 'f':
|
|
||||||
Sensor.framecount=0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'd': //Causes ISA object to print incoming CAN messages for debugging
|
|
||||||
Sensor.debug=!Sensor.debug;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'D': //Causes ISA object to print derived values for debugging
|
|
||||||
Sensor.debug2=!Sensor.debug2;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'i':
|
|
||||||
Sensor.initialize();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '?': //Print a menu describing these functions
|
|
||||||
printMenu();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '1':
|
|
||||||
Sensor.STOP();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '3':
|
|
||||||
Sensor.START();
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void getRate()
|
|
||||||
{
|
|
||||||
Serial<<"\n Enter the Data Rate in Kbps you want for CAN : ";
|
|
||||||
while(Serial.available() == 0){}
|
|
||||||
float V = Serial.parseFloat();
|
|
||||||
if(V>0)
|
|
||||||
{
|
|
||||||
Serial<<"Datarate:"<<V<<"\n\n";
|
|
||||||
uint8_t rate=V;
|
|
||||||
|
|
||||||
datarate=V*1000;
|
|
||||||
|
|
||||||
Sensor.begin(port,datarate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void getPort()
|
|
||||||
{
|
|
||||||
Serial<<"\n Enter port selection: c0=CAN0 c1=CAN1 ";
|
|
||||||
while(Serial.available() == 0){}
|
|
||||||
int P = Serial.parseInt();
|
|
||||||
if(P>1) Serial<<"Entry out of range, enter 0 or 1 \n";
|
|
||||||
else
|
|
||||||
{
|
|
||||||
port=P;
|
|
||||||
Sensor.begin(port,datarate);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue