├── .gitignore ├── library.properties ├── examples └── Flatpack2-Status │ └── Flatpack2-Status.ino ├── README.md └── src ├── Flatpack2.h └── Flatpack2.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Flatpack2 2 | version=0.0.1 3 | author=Tom van Klinken 6 | 7 | 8 | void onUpdate(int i) 9 | { 10 | Serial.print("Received status from power supply "); 11 | Serial.print(Flatpack2::units[i].id); 12 | Serial.print(" ("); 13 | Serial.print(Flatpack2::units[i].serialStr); 14 | Serial.print(")"); 15 | 16 | Serial.print(" status="); 17 | Serial.print(Flatpack2::units[i].status); 18 | 19 | Serial.print(" intake_temp="); 20 | Serial.print(Flatpack2::units[i].intake_temp); 21 | 22 | Serial.print(" output_temp="); 23 | Serial.print(Flatpack2::units[i].output_temp); 24 | 25 | Serial.print(" input_voltage="); 26 | Serial.print(Flatpack2::units[i].input_voltage); 27 | 28 | Serial.print(" output_current="); 29 | Serial.print(Flatpack2::units[i].output_current); 30 | 31 | Serial.print(" output_voltage="); 32 | Serial.print(Flatpack2::units[i].output_voltage); 33 | 34 | Serial.println(); 35 | } 36 | 37 | void setup() { 38 | Serial.begin(115200); 39 | while (!Serial); 40 | 41 | LOG_INFO("Flatpack2 example"); 42 | 43 | // Set CAN options for your board here 44 | #ifndef ARDUINO_ARCH_ESP32 45 | CAN.setClockFrequency(8E6); /* 8Mhz clock frequency for Arduino UNO */ 46 | #endif 47 | 48 | // Start communication with Flatpack2 49 | Flatpack2::onUpdate = &onUpdate; 50 | Flatpack2::Start(); 51 | } 52 | 53 | void loop() { 54 | // Nothing 55 | } 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flatpack2 2 | 3 | Arduino Library for communicating with Eltek Flatpack2 power supplies. 4 | 5 | I found multiple pieces of code for communicating with Flatpack2 power supplies 6 | but I wanted a library so my program code would be more clean. 7 | 8 | ### Features 9 | - Automaticly login and assign ID to powersupplies on CAN bus 10 | - Store last status in an object 11 | - Call an callback on status request 12 | - Send a broadcast to change voltage & current 13 | 14 | ### TODO 15 | - Test with other revisions and multiple devices on bus 16 | - Test current limit 17 | - Add bootup voltage 18 | - Cleanup receive code & debug messages 19 | 20 | ### Thanks to 21 | - https://github.com/the6p4c/Flatpack2 22 | - https://github.com/Lennart-O/ESP32_ELTEK_FLATPACK 23 | - https://github.com/neg2led/flatpack2s2 24 | - https://electricmotorcycleforum.com/boards/index.php?topic=6405.135;wap2 25 | - https://www.elithion.com/lithiumate/php/eltek.php 26 | - https://dta0yqvfnusiq.cloudfront.net/conle38747825/2018/07/Flatpack2-HE-Front-End-5b44b5d5e3926.pdf 27 | 28 | ### Dependencies 29 | - https://github.com/sandeepmistry/arduino-CAN 30 | - https://github.com/hideakitai/DebugLog 31 | 32 | ### Status 33 | Code is very alpha currently. 34 | 35 | ## Install 36 | ```sh 37 | cd ~/Documents/Arduino/libraries/ 38 | git clone https://github.com/tomvanklinken/Flatpack2 Flatpack2 39 | ``` 40 | 41 | ### Compatibility 42 | 43 | | Description | Part number | Revision | Status | Set voltage | Set current | Bootup voltage | 44 | | ----------------------------- | ----------- | -------- | ------ | ----------- |------------ | -------------- | 45 | | FP2 48V 3KW FRONT END RECT HE | 380875 | 1.10 | YES | YES | UNTESTED | UNTESTED | 46 | 47 | -------------------------------------------------------------------------------- /src/Flatpack2.h: -------------------------------------------------------------------------------- 1 | #ifndef Flatpack2_h 2 | #define Flatpack2_h 3 | 4 | #include 5 | #include 6 | 7 | #define CAN_SPEED 125E3 /* CAN Speed 125 kbps */ 8 | #define FLATPACK2_MAX_UNITS 4 /* Maximum number of chargers */ 9 | 10 | enum FLATPACK2_STATUS 11 | { 12 | FLATPACK2_STATUS_NORMAL, // 04 normal voltage reached 13 | FLATPACK2_STATUS_CURRENT_LIMIT, // 08 current-limiting active 14 | FLATPACK2_STATUS_WALK_IN, // 10 Walk in (voltage ramping up) 15 | FLATPACK2_STATUS_ALARM, // 0C Alarm 16 | FLATPACK2_STATUS_UNKNOWN // unknown response 17 | }; 18 | 19 | enum FLATPACK2_WALKIN 20 | { 21 | FLATPACK2_WALKIN_5, // 5 second walk-in 22 | FLATPACK2_WALKIN_60 // 60 second walk-in 23 | }; 24 | 25 | struct FLATPACK2_UNIT { 26 | int id; // ID of charger (first charger is 1, second is 2, etc) 27 | uint8_t serial[6]; // Serialnumber in binary format 28 | char serialStr[13]; // Serialnumber in string format 29 | unsigned long lastupdate; // Last time object was updated (millis()) 30 | unsigned long lastlogin; // Last time login packet was send 31 | FLATPACK2_STATUS status; // Current status of charger 32 | int intake_temp; // Temperature in centigrade of intake 33 | int output_temp; // Temperature in centigrade of exhaust 34 | int input_voltage ; // Input voltage in volts 35 | int output_current; // Output current in deciamps (10 deciamps = 1 amp) 36 | int output_voltage; // Output voltage in centivolts (100 centivolt = 1 volt) 37 | }; 38 | 39 | class Flatpack2 40 | { 41 | public: 42 | Flatpack2(); 43 | static void Start(); 44 | static void setOutput(int, int, int, FLATPACK2_WALKIN = FLATPACK2_WALKIN_5); 45 | 46 | static /*inline*/ FLATPACK2_UNIT units[FLATPACK2_MAX_UNITS]; 47 | static /*inline*/ int units_count/* = 0*/ ; 48 | static /*inline*/ void (*onUpdate)(int)/* = 0*/; 49 | private: 50 | static void onReceive(int ); 51 | static void sendLogin(uint8_t *, int ); 52 | static void sendCAN(long , uint8_t * , int, bool = false ); 53 | static void toHex(char *dst, uint8_t *data, int len); 54 | static void toHexReverse(char *dst, uint8_t *data, int len); 55 | }; 56 | 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /src/Flatpack2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | int Flatpack2::units_count = 0 ; 5 | void (*Flatpack2::onUpdate)(int) = NULL; 6 | FLATPACK2_UNIT Flatpack2::units[FLATPACK2_MAX_UNITS]; 7 | 8 | 9 | Flatpack2::Flatpack2() 10 | { 11 | // Not used as we are using only static methods 12 | } 13 | void Flatpack2::Start() 14 | { 15 | // Start CAN communication 16 | if (!CAN.begin(CAN_SPEED)) { 17 | LOG_ERROR("Starting CAN failed!"); 18 | while (1); 19 | } 20 | CAN.onReceive(Flatpack2::onReceive); 21 | } 22 | void Flatpack2::toHex(char *dst, uint8_t *data, int len) 23 | { 24 | for(int i=0;i> 8; 95 | data[2] /* output voltage 1 */ = voltage & 0x00ff; 96 | data[3] /* output voltage 1 */ = (voltage & 0xff00) >> 8; 97 | data[4] /* output voltage 2 */ = voltage & 0x00ff; 98 | data[5] /* output voltage 2 */ = (voltage & 0xff00) >> 8; 99 | data[6] /* overvoltage */ = overvoltage & 0x00ff; 100 | data[7] /* overvoltage */ = (overvoltage & 0xff00) >> 8; 101 | Flatpack2::sendCAN(packet_id, data, 8); 102 | } 103 | 104 | void Flatpack2::onReceive(int packetSize) 105 | { 106 | char output_msg[256]; 107 | char tmp[32]; 108 | 109 | LOG_INFO("Flatpack2::onReceive()"); 110 | 111 | //strcpy(output_msg, "Received "); 112 | 113 | 114 | if (CAN.packetExtended()) { 115 | //strcat(output_msg, "extended "); 116 | } 117 | 118 | if (CAN.packetRtr()) { 119 | // Remote transmission request, packet contains no data 120 | // strcat(output_msg, "RTR "); 121 | } 122 | 123 | //strcat(output_msg, "packet with id 0x"); 124 | //sprintf(tmp,"%08X", CAN.packetId()); 125 | //strcat(output_msg, tmp); 126 | 127 | 128 | if (CAN.packetRtr()) { 129 | //strcat(output_msg, " and requested length "); 130 | //sprintf(tmp,"%i", CAN.packetDlc()); 131 | //strcat(output_msg, tmp); 132 | } else { 133 | //strcat(output_msg, " and length "); 134 | //sprintf(tmp,"%i", packetSize); 135 | //strcat(output_msg, tmp); 136 | 137 | uint32_t packet_id = CAN.packetId(); 138 | 139 | // CAN Bus introduction 140 | if ( (packet_id & 0xFFFF0000) == 0x05000000 && packetSize == 8) 141 | { 142 | uint8_t packet[8]; 143 | for(int i=0;i<8;i++) 144 | packet[i]=CAN.read(); 145 | //for(int i=0;i<8;i++) {Serial.print(packet[i],HEX);Serial.print(" ");}Serial.println(); 146 | 147 | if (packet[0] == 0x1B) // Ignore packet[7] this sometimes seems to count up 148 | { 149 | uint8_t serialNumber[6]; 150 | char serialNumberStr[13]; 151 | // Convert serialnumber to string 152 | for(int i=0;i<6;i++) 153 | { 154 | serialNumber[i] = packet[i + 1]; 155 | serialNumberStr[i*2 + 0] = ((packet[i + 1] & 0xf0) >> 4) + 48; 156 | serialNumberStr[i*2 + 1] = (packet[i + 1] & 0x0f) + 48; 157 | } 158 | serialNumberStr[12] = '\0'; 159 | 160 | 161 | LOG_INFO("Received CAN BUS Introduction packet from",serialNumberStr); 162 | 163 | // Search for charger 164 | int i,id = -1; 165 | for(i = 0; i< Flatpack2::units_count; i++) 166 | { 167 | if (memcmp(Flatpack2::units[i].serial,serialNumber,6) == 0) 168 | { 169 | id = Flatpack2::units[i].id; 170 | break; 171 | } 172 | } 173 | if (id == -1 && Flatpack2::units_count < FLATPACK2_MAX_UNITS) 174 | { 175 | i = Flatpack2::units_count; 176 | id = i + 1; // Array starts from 0, units starts from 1 177 | Flatpack2::units_count++; // Increment counter 178 | Flatpack2::units[i].id = id; 179 | memcpy(Flatpack2::units[i].serial, serialNumber, 6); 180 | strcpy(Flatpack2::units[i].serialStr, serialNumberStr); 181 | Flatpack2::units[i].lastupdate = 0; // Never updated 182 | Flatpack2::units[i].lastlogin = 0; // Never logged in 183 | } 184 | 185 | //LOG_INFO("Charger ",serialNumberStr, " has id ", id); 186 | 187 | if (id >= 0) 188 | { 189 | unsigned long t = millis(); 190 | if (Flatpack2::units[i].lastlogin == 0 || (t-Flatpack2::units[i].lastlogin) > 10000) 191 | { 192 | Flatpack2::sendLogin(serialNumber,id); 193 | Flatpack2::units[i].lastlogin = millis(); 194 | } else { 195 | LOG_INFO("Skipping login packet"); 196 | } 197 | } else { 198 | LOG_ERROR("Too many chargers ", Flatpack2::units_count); 199 | } 200 | /* 201 | 202 | 203 | { 204 | LOG_INFO("NIER HIER"); 205 | 206 | { 207 | id = Flatpack2::units[j].id; 208 | snprintf(str, 256, "Found charger %s with id %i", serialNumberStr, id); 209 | LOG_INFO(str); 210 | } 211 | } 212 | if (id == -1) 213 | { 214 | snprintf(str, 256, "Charger %s not found", serialNumberStr); 215 | LOG_INFO(str); 216 | }*/ 217 | 218 | } 219 | 220 | } else if ( (packet_id & 0xFF00FF00) == 0x05004000 && packetSize == 8) { 221 | // 222 | uint8_t id = (packet_id & 0x00FF0000) >> 16; 223 | uint8_t state = (packet_id & 0x000000FF) ; 224 | // 04 normal voltage reached 225 | // 08 current-limiting active 226 | // 10 Walk in (voltage ramping up) 227 | // 0C Alarm 228 | char str[256]; 229 | 230 | uint8_t data[8]; 231 | for(int i=0;i<8;i++) data[i] = CAN.read(); 232 | int intake_temp = data[0]; 233 | int output_temp = data[7]; 234 | int input_voltage = data[6] << 8 | data[5]; 235 | int output_current = data[2] << 8 | data[1]; 236 | int output_voltage = data[4] << 8 | data[3]; 237 | 238 | 239 | // Search for charger 240 | int i; 241 | bool found = false; 242 | for(i = 0; i< Flatpack2::units_count; i++) 243 | { 244 | if (Flatpack2::units[i].id == id) 245 | { 246 | found = true; 247 | Flatpack2::units[i].lastupdate = millis(); 248 | switch(state) 249 | { 250 | case 0x04: 251 | Flatpack2::units[i].status = FLATPACK2_STATUS_NORMAL; 252 | break; 253 | case 0x08: 254 | Flatpack2::units[i].status = FLATPACK2_STATUS_CURRENT_LIMIT; 255 | break; 256 | case 0x10: 257 | Flatpack2::units[i].status = FLATPACK2_STATUS_WALK_IN; 258 | break; 259 | case 0x0C: 260 | Flatpack2::units[i].status = FLATPACK2_STATUS_ALARM; 261 | break; 262 | default: 263 | Flatpack2::units[i].status = FLATPACK2_STATUS_UNKNOWN; 264 | break; 265 | } 266 | Flatpack2::units[i].intake_temp = intake_temp; 267 | Flatpack2::units[i].output_temp = output_temp; 268 | Flatpack2::units[i].input_voltage = input_voltage; 269 | Flatpack2::units[i].output_current = output_current; 270 | Flatpack2::units[i].output_voltage = output_voltage; 271 | // Call callback if available 272 | if (Flatpack2::onUpdate != NULL){ 273 | (*(Flatpack2::onUpdate))(i); 274 | } 275 | } 276 | } 277 | if (!found) 278 | { 279 | LOG_ERROR("Received status from charger ",id,", but charger not found"); 280 | } 281 | //if (first_status) 282 | //{ 283 | // first_status = false; 284 | //Flatpack2::setOutput(100, 5700, 5950); 285 | //Flatpack2::setOutput(10, 5200, 5950); 286 | // Flatpack2::setOutput(10, 4800, 5950); 287 | //} 288 | } else { 289 | 290 | LOG_INFO(output_msg); 291 | /* 292 | Serial.print("Data: "); 293 | // only print packet data for non-RTR packets 294 | while (CAN.available()) { 295 | Serial.print((char)CAN.read()); 296 | } 297 | Serial.println();*/ 298 | } 299 | } 300 | 301 | } --------------------------------------------------------------------------------