Nein, da muss nix probiert werden. Das löst man mit dem entsprechenden Code
Kommt ein zusammengesetztes Byte, werden alle enthaltenen Nachrichten generiert.
Nix, was Dich nervös machen muss.
können wir da designtechnisch eine kleine änderung machen, ich dachte wegen den fehlermeldungen, wär vielleicht sinvoll, getrennte fenster, auf status fehlermeldungen die vom motor kommen, und auf der chargeseite, die fehlermeldungen die von der bms kommen ??, was denkst
nachtrag:
ich hab noch byte 1 übersehen, da liegt auch was
Mich kann momentan nix erschüttern...
Dein Projekt - deine Zeit.
Dafür habe ich mich erstmal mit dem bisherigen befasst.
Was daraus geworden ist, gibt es später.
Vorher nochmal Deine LED-Lichterketten.
Was hab ich Dir gesagt? Kein delay!
An einem einzigen Beispiel kannst Du das selbst nachtesten.
Ich hab es für Dich kommentiert.
#include "FastLED.h"
#define NUM_LEDS 20
CRGB leds[NUM_LEDS];
void setup()
{
Serial.begin(115200);
FastLED.addLeds<WS2811, 14, BRG>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
FastLED.addLeds<WS2811, 15, BRG>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
// Serial2.begin(115200);
setAll(0, 0, 0);
}
void loop()
{
/*
int inByte = Serial2.read();
switch (inByte)
{
case 'p': //alles aus
setAll(0, 0, 0);
break;
case 'q': //grün einfaden
FadeIn(0, 255, 0);
break;
case 'r': //feuer
Fire(55, 120, 15);
break;
case 's': //Sparkle
Sparkle(255, 255, 255, 20);
break;
case 't': //scan
Scan(0, 255, 0, 1, 30, 50);
break;
default:
break;
}
*/
Scan(0, 255, 0, 1, 30, 50);
}
void FadeIn(byte red, byte green, byte blue)
{
float r, g, b;
for (int k = 0; k < 256; k = k + 1)
{
r = (k / 256.0) * red;
g = (k / 256.0) * green;
b = (k / 256.0) * blue;
setAll(r, g, b);
showStrip();
}
}
void Scan(byte red, byte green, byte blue, int EyeSize, int SpeedDelay, int ReturnDelay)
{
RightToLeft(red, green, blue, EyeSize, SpeedDelay, ReturnDelay);
}
void RightToLeft(byte red, byte green, byte blue, int EyeSize, int SpeedDelay, int ReturnDelay)
{
uint32_t startMillis = millis();
for (int i = NUM_LEDS - EyeSize - 2; i > 0; i--) // 20 - 1 - 2 = 18 Step
{
setAll(0, 0, 0);
setPixel(i, red / 10, green / 10, blue / 10);
for (int j = 1; j <= EyeSize; j++)
{
setPixel(i + j, red, green, blue);
}
setPixel(i + EyeSize + 1, red / 10, green / 10, blue / 10);
showStrip();
delay(SpeedDelay); // 18 Step * 30 ms = 540 ms!!!
}
delay(ReturnDelay); // 50 ms -> gesamt: 590 ms + 18 Übertragungen auf den stripe * 2ms
uint32_t endMillis = millis();
Serial.println(endMillis - startMillis);
}
void Sparkle(byte red, byte green, byte blue, int SpeedDelay)
{
int Pixel = random(NUM_LEDS);
setPixel(Pixel, red, green, blue);
showStrip();
delay(SpeedDelay);
setPixel(Pixel, 0, 0, 0);
}
void CylonBounce(byte red, byte green, byte blue, int EyeSize, int SpeedDelay, int ReturnDelay)
{
for (int i = 0; i < NUM_LEDS - EyeSize - 2; i++)
{
setAll(0, 0, 0);
setPixel(i, red / 10, green / 10, blue / 10);
for (int j = 1; j <= EyeSize; j++)
{
setPixel(i + j, red, green, blue);
}
setPixel(i + EyeSize + 1, red / 10, green / 10, blue / 10);
showStrip();
delay(SpeedDelay);
}
delay(ReturnDelay);
for (int i = NUM_LEDS - EyeSize - 2; i > 0; i--)
{
setAll(0, 0, 0);
setPixel(i, red / 10, green / 10, blue / 10);
for (int j = 1; j <= EyeSize; j++)
{
setPixel(i + j, red, green, blue);
}
setPixel(i + EyeSize + 1, red / 10, green / 10, blue / 10);
showStrip();
delay(SpeedDelay);
}
delay(ReturnDelay);
}
void Fire(int Cooling, int Sparking, int SpeedDelay)
{
static byte heat[NUM_LEDS];
int cooldown;
// Step 1. Cool down every cell a little
for ( int i = 0; i < NUM_LEDS; i++)
{
cooldown = random(0, ((Cooling * 10) / NUM_LEDS) + 2);
if (cooldown > heat[i])
{
heat[i] = 0;
}
else
{
heat[i] = heat[i] - cooldown;
}
}
// Step 2. Heat from each cell drifts 'up' and diffuses a little
for ( int k = NUM_LEDS - 1; k >= 2; k--)
{
heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2]) / 3;
}
// Step 3. Randomly ignite new 'sparks' near the bottom
if ( random(255) < Sparking )
{
int y = random(7);
heat[y] = heat[y] + random(160, 255);
}
// Step 4. Convert heat to LED colors
for ( int j = 0; j < NUM_LEDS; j++)
{
setPixelHeatColor(j, heat[j] );
}
showStrip();
delay(SpeedDelay);
}
void setPixelHeatColor (int Pixel, byte temperature)
{
// Scale 'heat' down from 0-255 to 0-191
byte t192 = round((temperature / 255.0) * 191);
// calculate ramp up from
byte heatramp = t192 & 0x3F; // 0..63
heatramp <<= 2; // scale up to 0..252
// figure out which third of the spectrum we're in:
if ( t192 > 0x80) // hottest
{
setPixel(Pixel, 255, 255, heatramp);
}
else if ( t192 > 0x40 ) // middle
{
setPixel(Pixel, 255, heatramp, 0);
}
else // coolest
{
setPixel(Pixel, heatramp, 0, 0);
}
}
void showStrip()
{
#ifndef ADAFRUIT_NEOPIXEL_H
// FastLED
FastLED.show();
#endif
}
void setPixel(int Pixel, byte red, byte green, byte blue)
{
#ifndef ADAFRUIT_NEOPIXEL_H
// FastLED
leds[Pixel].r = red;
leds[Pixel].g = green;
leds[Pixel].b = blue;
#endif
}
void setAll(byte red, byte green, byte blue)
{
for (int i = 0; i < NUM_LEDS; i++)
{
setPixel(i, red, green, blue);
}
showStrip();
}
Es wäre cool, wenn meine Rechnung mit dem tatsächlichen Ergebnis einigermassen passt.
Und für Deine Textausgaben musste ich erstmal tuef einatmen - immerhin hatten wir ja die Ausgaben bei einigen schon fix und fertig .
An und für sich ist das gar nicht so schlimm.
Und hier zeigt sich auch, warum das so wichtig ist, gleich von Anfang kleinteilig und wiederverwendbar zu arbeiten.
Nu hoffe ich nur, dass ich mich nicht ganz verhauen habe.
Evtl. musst Du noch Dinge korrigieren, die Du vorher schon mal gemacht hattest, aber ich nicht in meinem letzten Stand hatte...
/*
BMS_AKKU_letzter Stand 240311_1
Wird noch weiterentwickelt!
*/
#include <FlexCAN_T4.h>
#include <PWMServo.h>
//#define DEBUG // Wenn aktiviert, werden Zwischenwerte ausgegeben
#ifdef DEBUG
#define DBG_PRINT(...) Serial.print(__VA_ARGS__)
#define DBG_PRINTLN(...) Serial.println(__VA_ARGS__)
#else
#define DBG_PRINT(...)
#define DBG_PRINTLN(...)
#endif
FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_16> can1;
static CAN_message_t rxmsg, txmsg;
PWMServo myservo9;
PWMServo myservo10;
PWMServo myservo11;
PWMServo myservo12;
PWMServo soundservo[2] = { myservo9, myservo10 };
PWMServo driveSound[2] = { myservo11, myservo12 };
constexpr byte cellNums{ 96 };
struct CELLDATA // CAN-ID: 0x36
{
uint16_t voltage; // #1#2 Scaler 0.0001
uint16_t resistance; // #3#4 Scaler 0.01
uint16_t openVoltage; // #5#6 Scaler 0.0001
// uint8_t checkSum // #7 || Berechnung: CAN-ID + jedes Byte exclusive CRC-Byte + Länge inclusive CRC-Byte
};
//
constexpr byte thermoNums{ 32 };
struct COOLER // CAN-ID: 0x1838F3DE
{
int8_t temperature[thermoNums]; // #2
uint8_t enable; // #3 || & 0x80 FAULTSTATUS!!!
int8_t lowTemperature; // #4
int8_t highTemperature; // #5
// uint8_t highestId // #6 || ID dieses Moduls
// uint8_t lowestId // #7 || ID dieses Moduls
};
struct PACK
{
uint8_t stateOfCharge; // CAN-ID 0x0640 #0
uint16_t resistance; // CAN-ID 0x0640 #1#2 Scaler 0.001
uint8_t healty; // CAN-ID 0x0640 #3
uint16_t highestCellVoltage; // CAN-ID 0x0640 #4#5 Scaler 0.0001
uint16_t lowestCellVoltage; // CAN-ID 0x0640 #6#7 Scaler 0.0001
uint16_t maxDCLoad; // CAN-ID 0x0643 #0#1
uint16_t maxCCLoad; // CAN-ID 0x0643 #2#3
uint16_t absolutAmpere; // CAN-ID 0x0643 #4#5 Scaler 0.1
uint16_t absolutVoltage; // CAN-ID 0x0643 #6#7 Scaler 0.1
// flag => CAN-ID 0x200 -> Byte #0-> 4 Bit benutzt!!!
uint8_t flag; //mil, balancing, charging, powerReady || 4x NAN
// dtcFlag => CAN-ID 0x200 -> Byte #1-> 6 Bit benutzt!!!
uint8_t dtcFlag; //CellOverHIGHVoltage, cellUnderLowVoltage, currentFailure, ThermistorFault, wiringOpen, ChargeSafeRelaisFailure
};
/*
const char *flagObj[8] = {"p61.aph=", "p60.aph=", "t61.pco=", "t60.pco=", "NA", "NA", "NA", "NA"}; // NextionString je Wert
const uint16_t flagVal[8][2] = {{0, 0}, {0, 0}, {33808, 33808}, {33808, 2016}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}; // Value je BIT
*/
struct BATT
{
CELLDATA cell[cellNums]; // cellnum => #0 from CAN-ID: 0x36
CELLDATA oldcell[cellNums]; // Merker
COOLER cooler; // #0#1 || ID over all from CAN-ID: 0x1838F380
COOLER oldcooler; // Merker
PACK pack;
PACK oldpack;
} batt;
struct DRIVE
{
uint16_t rotations; // CAN-ID: 0x500 #0#1
uint16_t direction; // CAN-ID: 0x500 #2
uint8_t mode; // CAN-ID: 0x501 #0 // "off" "Run" "ManualStart"
uint8_t state; // CAN-ID: 0x501 #1
uint8_t heatsinkTemp; // CAN-ID: 0x501 #2
uint8_t engineTemp; // CAN-ID: 0x501 #3
uint16_t voltage; // CAN-ID: 0x502 #0#1
uint16_t ampere; // CAN-ID: 0x502 #2#3
uint8_t error; // CAN-ID: 0x502 #4
uint16_t power; // CAN-ID: 0x581 #5#6 // driveErrorText
};
DRIVE drive;
DRIVE oldDrive;
constexpr char valText[][4] {"p61", "p60", "t74", "t73",}; // ID200 - #0 -> 4 bit
constexpr char driveDirectionText[][2] {"N", "D", "R"}; // ID500 - #2
constexpr char driveModeText[][12] {"off", "Run", "ManualStart"};// ID501 - #0
constexpr char driveStateText[][14] // ID501 - #1 ACHTUNG! Basis 2
{
"WaitStart", "UdcLow", "UdcHigh", "UdcBelowUdcSw",
"UdcLim", "EmcyStop", "MProt", "PotPressed", "TmpHs",
};
constexpr char driveErrorText[][13] // ID502 - #4
{
"keine Fehler", "OVERCURRENT", "THROTTLE1", "THROTTLE2", "CANTIMEOUT", "EMCYSTOP",
"MPROT", "DESAT", "OVERVOLTAGE", "ENCODER", "PRECHARGE", "TMPHSMAX", "CURRENTLIMIT",
"PWMSTUCK", "CANCOUNTER", "HICUROFS2", "HIRESOFS", "LORESAMP", "TMPMMAX",
};
struct BMS
{
uint16_t dcdcTmp; // CAN-ID 0x0210 #2 DCDC konvertert Vorlauftemperatur
uint16_t dcdcinW; // CAN-ID 0x0210 #3 DCDC konvertert Leistungseingang in Watt
uint16_t dcdoutA; // CAN-ID 0x0210 #4 DCDC konvertert output in Ampere
uint16_t dcdoutV; // CAN-ID 0x0210 #5 DCDC konvertert output in Volt
uint16_t highTemperature; // CAN-ID 0x0641 #0#1
uint16_t lowTemperature; // CAN-ID 0x0641 #2#3
uint16_t internTemperature; // CAN-ID 0x0641 #4#5
uint16_t inputSupplyVoltage; // CAN-ID 0x0641 #6#7 Scaler 0.1
uint16_t adcIsolation; // CAN-ID 0x0642 #0#1
uint16_t shortestWave; // CAN-ID 0x0642 #2#3 Scaler 0.1
uint16_t avgTemperature; // CAN-ID 0x0642 #4#5
};
BMS bms;
BMS oldBms;
enum class MODUS { eco,
normal,
sport
};
MODUS modus = MODUS::eco;
//
constexpr byte kundrat{ 0x00 };
constexpr byte screem { 0x01 };
constexpr byte hund { 0x02 };
constexpr byte burb { 0x03 };
constexpr byte yeej { 0x04 };
constexpr byte laser { 0x05 };
constexpr byte bab { 0x06 };
constexpr byte pfiff { 0x07 };
struct SOUND
{
const byte servo;
const byte pos;
};
struct SPASSSOUND
{
SOUND sound[8];
bool isActive;
uint32_t startTime;
byte soundType;
} spasssound
{
{ { 0, 120, },
{ 0, 10, },
{ 0, 170, },
{ 0, 70, },
{ 1, 10, },
{ 1, 120, },
{ 1, 170, },
{ 1, 70, }
},
false, 0, 0,
};
byte nextionPage;
byte pageElement;
//
char nextionBuf[50] = { '\0' }; // Zwischenspeicher für Übertragung zum Nextion
bool isNextionError = false; // Erkennung auf 0x1A vom Nextion
void setup()
{
delay(4000);
Serial.begin(115200); // Serieller Monitor
Serial2.begin(115200); // Nextion
soundservo[0].attach(9); // Drehzahl
soundservo[1].attach(10); // Last
driveSound[0].attach(11); // Soundpool 1
driveSound[1].attach(12); // Soundpool 2
pinMode(LED_BUILTIN, OUTPUT);
can1.begin();
can1.setBaudRate(500000);
defaultModeMessage(); // füllt txmsg
can1.write(txmsg);
soundservo[0].write(90);
soundservo[1].write(90);
driveSound[0].write(0);
driveSound[1].write(0);
}
void loop(void)
{
canReadIn();
nextReadIn();
nextionPages();
driverSound();
setSound();
sendHeartbeatMsg(500);
}
void nextionPages()
{
// *INDENT-OFF*
switch (nextionPage) {
case 'A': homePage(); break; // Hauptseite
case 'B': pageOne(); break; // Zellspannungen
case 'C': pageTwo(); break; // Zelltemperaturen
case 'D': pageThree(); break; // übersicht alle Temperaturen
case 'E': pageFour(); break; // Leistungsvorgabe
case 'F': pageFive(); break; // Akkuzustand
case 'G': pageSix(); break; // Iso Überwachung - vollständig
case 'H': pageSeven(); break; // Lichtsteuerung
case 'I': pageEight(); break; // Motordata - vollständig
case 'J': pageNine(); break; // Sound
default: nextionPage = 'A'; break;
}
pageElement++;
}
//
void homePage() { //Hauptbildschirm
// *INDENT-OFF*
switch (pageElement) {
case 0: sendData("n1", batt.pack.stateOfCharge, batt.oldpack.stateOfCharge); break;
case 1: sendDataScaler("n2", batt.pack.absolutVoltage, batt.oldpack.absolutVoltage, 10); break;
case 2: sendDataScaler("n3", batt.pack.absolutAmpere, batt.oldpack.absolutAmpere, 10); break;
// case 3: sendData("t4", drive.direction, oldDrive.direction); break;
case 3: sendText("t4", drive.direction, oldDrive.direction, driveDirectionText[drive.direction]); break;
case 4: sendData("x1", batt.cooler.highTemperature, batt.oldcooler.highTemperature); break;
case 5: sendCellDifferent("x143"); break;
default: pageElement = 0xFF; break;
}
// *INDENT-ON*
}
//
void pageOne() //zellspannungen
{
// *INDENT-OFF*
switch (pageElement) {
case 0 ...(cellNums - 1): sendDataScaler("c", pageElement, batt.cell[pageElement].voltage, batt.oldcell[pageElement].voltage, 10); break;
case cellNums: sendDataScaler("x100", batt.pack.highestCellVoltage, batt.oldpack.highestCellVoltage, 10); break;
case cellNums + 1: sendDataScaler("x101", batt.pack.lowestCellVoltage, batt.oldpack.lowestCellVoltage, 10); break;
case cellNums + 2: sendCellDifferent("x143"); break;
default: pageElement = 0xFF; break;
}
// *INDENT-ON*
}
//
void pageTwo() //zelltemperaturen
{
// *INDENT-OFF*
const byte myByte = thermoNums - 1;
switch (pageElement) {
case 0 ... myByte:
sendData("d", pageElement, batt.cooler.temperature[pageElement], batt.oldcooler.temperature[pageElement]);
break;
case myByte + 1: sendData("x135", batt.cooler.lowTemperature, batt.oldcooler.lowTemperature); break;
case myByte + 2: sendData("x136", batt.cooler.highTemperature, batt.oldcooler.highTemperature); break;
case myByte + 3: sendData("x137", bms.avgTemperature, oldBms.avgTemperature); break;
default: pageElement = 0xFF; break;
}
// *INDENT-ON*
}
//
void pageThree() //temperaturen
{
// *INDENT-OFF*
switch (pageElement) {
case 0: sendData("x135", batt.cooler.lowTemperature, batt.oldcooler.lowTemperature); break;
case 1: sendData("x136", batt.cooler.highTemperature, batt.oldcooler.highTemperature); break;
case 2: sendData("x141", drive.heatsinkTemp, oldDrive.heatsinkTemp); break;
case 3: sendData("x142", drive.engineTemp, oldDrive.engineTemp); break;
case 4: sendData("x137", bms.dcdcTmp, oldBms.dcdcTmp); break;
default: pageElement = 0xFF; break;
}
// *INDENT-ON*
}
//
void pageFour() //power
{
// *INDENT-OFF*
switch (pageElement) {
case 0:
switch (modus) {
case MODUS::eco: break;
case MODUS::normal: break;
case MODUS::sport: break;
}
break;
case 1: sendDrivePower(); break;
default: pageElement = 0xFF; break;
}
// *INDENT-ON*
}
//
void pageFive() //charger
{
// *INDENT-OFF*
switch (pageElement) {
case 0: sendData("n6", batt.pack.stateOfCharge, batt.oldpack.stateOfCharge); break;
case 1: sendDataScaler("n7", batt.pack.absolutVoltage, batt.oldpack.absolutVoltage, 10); break;
case 2: sendDataScaler("n8", batt.pack.absolutAmpere, batt.oldpack.absolutAmpere, 100); break;
case 3: sendCellDifferent("x2"); break;
case 4: sendData("n9", batt.pack.healty, batt.oldpack.healty); break;
case 5: sendData("x146", batt.cooler.lowTemperature, batt.oldcooler.lowTemperature); break;
case 6: sendData("x147", batt.cooler.highTemperature, batt.oldcooler.highTemperature); break;
case 7: sendDataScaler("x148", batt.pack.highestCellVoltage, batt.oldpack.highestCellVoltage, 10); break;
case 8: sendDataScaler("x149", batt.pack.lowestCellVoltage, batt.oldpack.lowestCellVoltage, 10); break;
case 9: sendData("x150", bms.inputSupplyVoltage, oldBms.inputSupplyVoltage); break;
case 10: sendData("x151", bms.internTemperature, oldBms.internTemperature); break;
case 11: packFlag(); break;
default: pageElement = 0xFF; break;
}
// *INDENT-ON*
}
void packFlag()
{
static byte checkBit = 0;
if (batt.pack.flag != batt.oldpack.flag) // Wert ist geändert
{
bool myBit = bitRead(batt.pack.flag, checkBit); // Zwischenspeicher
if (myBit != bitRead(batt.oldpack.flag, checkBit)) // bit ist geändert
{
if (myBit) // wenn gesetzt
{
sprintf(nextionBuf, "%s%s", valText[checkBit], ".aph=127");
bitSet(batt.oldpack.flag, checkBit);
}
else // wenn gelöscht
{
sprintf(nextionBuf, "%s%s", valText[checkBit], ".aph=0");
bitClear(batt.oldpack.flag, checkBit);
}
sendData2Nextion(nextionBuf);
}
if (++checkBit > sizeof(valText) / sizeof(valText[0])) // Zählen auf Basis der eingepflgten Texte
{ checkBit = 0; }
}
}
//
void pageSix() //iso
// *INDENT-OFF*
{
switch (pageElement) {
case 0: sendData("x152", bms.adcIsolation, oldBms.adcIsolation); break;
case 1: sendData("x153", bms.shortestWave, oldBms.shortestWave); break;
default: pageElement = 0xFF; break;
}
// *INDENT-ON*
}
//
void pageSeven() //light
{
switch (pageElement)
{
case 0:
if (digitalRead(LED_BUILTIN))
{
}
else
{
}
break;
default:
pageElement = 0xFF;
break;
}
}
//
void pageEight() //status
{
// *INDENT-OFF*
switch (pageElement) {
// case 0: sendData("t77", drive.mode, oldDrive.mode); break;
case 0: sendText("t77", drive.mode, oldDrive.mode, driveModeText[drive.mode]); break;
// case 1: sendData("t79", drive.state, oldDrive.state); break;
case 1: sendDriveState("t79");break;
case 2: sendDataScaler("n10", drive.voltage, oldDrive.voltage, 255); break;
case 3: sendData("n11", drive.ampere, oldDrive.ampere); break;
case 4: sendDataScaler("n12", drive.rotations, oldDrive.rotations, 255); break;
case 5: sendData("x154", bms.dcdcinW, oldBms.dcdcinW); break;
case 6: sendData("x155", bms.dcdoutA, oldBms.dcdoutA); break;
case 7: sendData("x156", bms.dcdoutV, oldBms.dcdoutV); break;
case 8: sendText("t20", drive.error, oldDrive.error, driveErrorText[drive.error]); break;
default: pageElement = 0xFF; break;
}
// *INDENT-ON*
}
//
void sendDriveState(const char *text)
{
static byte checkBit = 0;
if (drive.state != oldDrive.state)
{
sprintf(nextionBuf, "%s%s%s", text, ".", driveStateText[0]);
bool c = bitRead(drive.state, checkBit);
if (c != bitRead(oldDrive.state, checkBit))
{
if (c)
{
sprintf(nextionBuf, "%s%s%s", text, ".", driveStateText[checkBit + 1]);
}
bitSet(oldDrive.state, checkBit);
}
}
if (++checkBit > sizeof(driveStateText) / sizeof(driveStateText[0]))
{ checkBit = 0; }
}
//
void sendText(const char *txt, uint8_t value, uint8_t oldValue, const char *valText)
{
if (value != oldValue)
{
sprintf(nextionBuf, "%s%s%s", txt, ".txt=", valText);
sendData2Nextion(nextionBuf);
oldValue = value;
}
}
//
void pageNine() //sound
{
switch (pageElement)
{
/*
case 0:
switch (soundType)
{
case kundrat:
break;
case screem:
break;
case hund:
break;
case burb:
break;
case yeej:
break;
case bab:
break;
case pfiff:
break;
}
break;
*/
default:
pageElement = 0xFF;
break;
}
}
//
void canReadIn()
{
// *INDENT-OFF*
if (can1.read(rxmsg)) {
switch (rxmsg.id) {
case 0x36:
if (rxmsg.buf[0] < cellNums) // cellID passt noch als Element
{
batt.cell[rxmsg.buf[0]].voltage = rxmsg.buf[1] << 8 | rxmsg.buf[2];
batt.cell[rxmsg.buf[0]].resistance = rxmsg.buf[3] << 8 | rxmsg.buf[4];
batt.cell[rxmsg.buf[0]].openVoltage = rxmsg.buf[5] << 8 | rxmsg.buf[6];
/*
uint8_t crc = rxmsg.id;
for (byte b = 0; b < 7; b++)
{ crc += rxmsg.buf[b]; }
crc += 8;
Serial.print(F("cell CRC "));
if (rxmsg.buf[7] != crc)
{ Serial.print(F("nicht ")); }
Serial.println("OK");
*/
}
break;
case 0x200:
batt.pack.flag = rxmsg.buf[0];
batt.pack.dtcFlag = rxmsg.buf[1];
break;
case 0x210:
bms.dcdcTmp = rxmsg.buf[2];
bms.dcdcinW = rxmsg.buf[3];
bms.dcdoutA = rxmsg.buf[4];
bms.dcdoutV = rxmsg.buf[5];
break;
case 0x500:
drive.rotations = rxmsg.buf[0] << 8 | rxmsg.buf[1];
drive.direction = rxmsg.buf[2] << 8 | rxmsg.buf[3];
break;
case 0x501:
drive.mode = rxmsg.buf[0];
drive.state = rxmsg.buf[1];
drive.heatsinkTemp = rxmsg.buf[2];
drive.engineTemp = rxmsg.buf[3];
break;
case 0x502:
drive.voltage = rxmsg.buf[0] << 8 | rxmsg.buf[1];
drive.ampere = rxmsg.buf[2] << 8 | rxmsg.buf[3];
break;
case 0x581:
drive.power = rxmsg.buf[5] << 8 | rxmsg.buf[6];
break;
case 0x640:
batt.pack.stateOfCharge = rxmsg.buf[0];
batt.pack.resistance = rxmsg.buf[1] << 8 | rxmsg.buf[2];
batt.pack.healty = rxmsg.buf[3];
batt.pack.highestCellVoltage = rxmsg.buf[4] << 8 | rxmsg.buf[5];
batt.pack.lowestCellVoltage = rxmsg.buf[6] << 8 | rxmsg.buf[7];
break;
case 0x641:
bms.highTemperature = rxmsg.buf[0] << 8 | rxmsg.buf[1];
bms.lowTemperature = rxmsg.buf[2] << 8 | rxmsg.buf[3];
bms.internTemperature = rxmsg.buf[4] << 8 | rxmsg.buf[5];
bms.inputSupplyVoltage = rxmsg.buf[6] << 8 | rxmsg.buf[7];
break;
case 0x642:
bms.adcIsolation = rxmsg.buf[0] << 8 | rxmsg.buf[1];
bms.shortestWave = rxmsg.buf[2] << 8 | rxmsg.buf[3];
bms.avgTemperature = rxmsg.buf[4] << 8 | rxmsg.buf[5];
break;
case 0x643:
batt.pack.maxDCLoad = rxmsg.buf[0] << 8 | rxmsg.buf[1];
batt.pack.maxCCLoad = rxmsg.buf[2] << 8 | rxmsg.buf[3];
batt.pack.absolutAmpere = rxmsg.buf[4] << 8 | rxmsg.buf[5];
batt.pack.absolutVoltage = rxmsg.buf[6] << 8 | rxmsg.buf[7];
break;
case 0x1838F3DE:
uint16_t myId = rxmsg.buf[0] << 8 | rxmsg.buf[1];
if (myId < thermoNums) // ID passt noch als Element
{
batt.cooler.temperature[myId] = rxmsg.buf[2];
batt.cooler.lowTemperature = rxmsg.buf[4];
batt.cooler.highTemperature = rxmsg.buf[5];
}
break;
}
}
// *INDENT-ON*
}
//
bool checkNextionError(const byte myByte)
{
bool ret = false;
const char check[] = { 0x1A, 0xFF, 0xFF, 0xFF }; // Vergleichspuffer
constexpr byte len{ sizeof(check) / sizeof(check[0]) };
static char buf[len] = { '\0' }; // Puffer
buf[len - 1] = myByte; // Zeichen aufnehmen
// Prüfung ob vom Nextion eine Ablehnung kam
if (strncmp(buf, check, len)) // Vergleich != 0?
{
Serial.println(F("NextionError!"));
ret = true;
}
for (byte b = 0; b < len - 1; b++) // Zeichen im buffer verschieben
{ buf[b] = buf[b + 1]; }
return ret;
}
//
void nextReadIn()
{
static byte errorCounter = 0;
uint8_t x = Serial2.available(); // Es kommt was vom Nextion
while (x--)
{
byte myByte = Serial2.read(); // Übernehme 1 zeichen
hexPrint(myByte); // Kontrolle auf dem SerMon was ankommt
defaultModeMessage(); // Füllt txmsg mit defaultwerten
switch (myByte) //
{
case '1': // entspricht default
modus = MODUS::eco;
can1.write(txmsg);
break;
case '2':
modus = MODUS::normal;
txmsg.buf[5] = 0x4A;
can1.write(txmsg);
break;
case '3':
modus = MODUS::sport;
txmsg.buf[5] = 0x7D;
can1.write(txmsg);
break;
case 'g' ... 'n':
spasssound.soundType = myByte - 0x67; // soundType wird 0 - 7
spasssound.isActive = true;
spasssound.startTime = millis();
break;
case 'y':
digitalWrite(LED_BUILTIN, LOW);
break;
case 'x':
digitalWrite(LED_BUILTIN, HIGH);
break;
case 'A' ... 'H':
nextionPage = myByte;
pageElement = 0xFF;
break;
case 0x1A: // Achtung, das ist ein Workaround, weil default noch nicht getestet ist!!!
//errorCounter = 5;
break;
default:
/*
if (checkNextionError(myByte))
{ errorCounter = 5; }
*/
break;
}
if (errorCounter)
{
isNextionError = true;
if (!pageElement)
{
errorCounter--;
}
}
else
{
isNextionError = false;
}
}
}
//
void setSound()
{
static byte step = 0;
if (spasssound.isActive)
{
switch (step)
{
case 0:
DBG_PRINT(F("neuer Sound: "));
DBG_PRINTLN(spasssound.soundType);
DBG_PRINT(F("Gebe aus (Servo / out): "));
DBG_PRINT(spasssound.sound[spasssound.soundType].servo);
DBG_PRINT('\t');
DBG_PRINTLN(spasssound.sound[spasssound.soundType].pos);
soundservo[spasssound.sound[spasssound.soundType].servo].write(spasssound.sound[spasssound.soundType].pos);
step++;
break;
case 1:
if (millis() - spasssound.startTime > 30)
{
soundservo[spasssound.sound[spasssound.soundType].servo].write(90);
step++;
}
break;
case 2:
if (millis() - spasssound.startTime > 60)
{ step++; }
break;
default:
spasssound.isActive = false;
break;
}
}
else if (step)
{
step = 0;
soundservo[0].write(90);
soundservo[1].write(90);
}
}
//
void sendDrivePower()
{
if (drive.power != oldDrive.power || isNextionError)
{
uint16_t powerData = ((((static_cast<float>(drive.power) / 32) * batt.pack.absolutVoltage) * 1.36) / 10000);
sprintf(nextionBuf, "%s%i", "n5.val=", powerData);
sendData2Nextion(nextionBuf);
oldDrive.power = drive.power;
}
}
//
void sendCellDifferent(const char *var)
{
if ((batt.pack.highestCellVoltage != batt.oldpack.highestCellVoltage) || (batt.pack.lowestCellVoltage != batt.oldpack.lowestCellVoltage) || isNextionError)
{
uint16_t different = batt.pack.highestCellVoltage - batt.pack.lowestCellVoltage;
sprintf(nextionBuf, "%s%s%i", var, ".val=", different / 10);
sendData2Nextion(nextionBuf);
batt.oldpack.highestCellVoltage = batt.pack.highestCellVoltage;
batt.oldpack.lowestCellVoltage = batt.pack.lowestCellVoltage;
}
}
//
void sendDataScaler(const char *title, const uint16_t idx, const uint16_t value, uint16_t &oldValue, const uint16_t &scaler) // Nextion -> unsigned int16
{
if (value != oldValue || isNextionError)
{
sprintf(nextionBuf, "%s%i%s%i", title, idx, ".val=", value / scaler);
sendData2Nextion(nextionBuf);
oldValue = value;
}
}
//
void sendDataScaler(const char *title, const uint16_t value, uint16_t &oldValue, const uint16_t &scaler) // Nextion -> unsigned int16
{
if (value != oldValue || isNextionError)
{
sprintf(nextionBuf, "%s%s%i", title, ".val=", value / scaler);
sendData2Nextion(nextionBuf);
oldValue = value;
}
}
//
void sendData(const char *title, const uint16_t idx, const uint16_t value, uint16_t &oldValue) // Nextion -> unsigned int16
{
if (value != oldValue || isNextionError)
{
sprintf(nextionBuf, "%s%i%s%i", title, idx, ".val=", value);
sendData2Nextion(nextionBuf);
oldValue = value;
}
}
//
void sendData(const char *title, const uint16_t idx, const int16_t value, int16_t &oldValue) // Nextion -> signed int16
{
if (value != oldValue || isNextionError)
{
sprintf(nextionBuf, "%s%i%s%i", title, idx, ".val=", value);
sendData2Nextion(nextionBuf);
oldValue = value;
}
}
//
void sendData(const char *title, const uint16_t idx, const int8_t value, int8_t &oldValue) // Nextion -> signed int8
{
if (value != oldValue || isNextionError)
{
sprintf(nextionBuf, "%s%i%s%i", title, idx, ".val=", value);
sendData2Nextion(nextionBuf);
oldValue = value;
}
}
//
void sendData(const char *title, const uint16_t value, uint16_t &oldValue)
{
if (value != oldValue || isNextionError)
{
sprintf(nextionBuf, "%s%s%i", title, ".val=", value);
sendData2Nextion(nextionBuf);
oldValue = value;
}
}
//
void sendData(const char *title, const uint8_t value, uint8_t &oldValue)
{
if (value != oldValue || isNextionError)
{
sprintf(nextionBuf, "%s%s%i", title, ".val=", value);
sendData2Nextion(nextionBuf);
oldValue = value;
}
}
//
void sendData(const char *title, const int8_t value, int8_t &oldValue) // Nextion -> signed int8
{
if (value != oldValue || isNextionError)
{
sprintf(nextionBuf, "%s%s%i", title, ".val=", value);
sendData2Nextion(nextionBuf);
oldValue = value;
}
}
// Nextion Ende ist 3* 0xFF
void nextionSendEnd()
{
for (byte b = 0; b < 3; b++)
{
Serial2.write(0xFF);
}
DBG_PRINTLN();
}
//
/*
void sendData2Nextion(const char *sendBuf, const uint16_t value)
{
Serial2.print(sendBuf);
Serial2.print(value);
Serial.print(sendBuf);
Serial.print(value);
nextionSendEnd();
}
*/
//
void sendData2Nextion(const char *sendBuf)
{
Serial2.print(sendBuf);
DBG_PRINT(sendBuf);
nextionSendEnd();
}
//
void defaultModeMessage() // Vorbelegung
{
txmsg.id = 0x601;
txmsg.len = 8;
txmsg.buf[0] = 0x23;
txmsg.buf[1] = 0x00;
txmsg.buf[2] = 0x20;
txmsg.buf[3] = 0x31;
txmsg.buf[4] = 0x00;
txmsg.buf[5] = 0x26;
txmsg.buf[6] = 0x00;
txmsg.buf[7] = 0x00;
}
//
void sendHeartbeatMsg(const uint32_t intervall) //Ladevorgabe Bleibatterie (DC/DC 400V => 14.4V,alle 500ms)
{
static uint32_t lastSendTime = 0;
if (millis() - lastSendTime > intervall)
{
txmsg.id = 0x3D8;
txmsg.len = 3;
txmsg.buf[0] = 0x14;
txmsg.buf[1] = 0x07;
txmsg.buf[2] = 0x00;
can1.write(txmsg);
lastSendTime = millis();
}
}
//
void driverSound()
{
uint8_t value = drive.rotations / 360; // Upm umrechnen
if (!value && drive.rotations) // Prüfung auf Mindestwert
{ value = 1; } // Mindestwert setzen
driveSound[0].write(value); // ausgeben
value = drive.ampere / 1.4; // Last umrechnen
if (!value && drive.ampere) // Prüfung auf Mindestwert
{ value = 1; } // setzen
driveSound[1].write(value); // ausgeben
}
//
void hexPrint(const byte myByte) // gibt das übergebene Byte im Format "0xBB, " aus
{
static byte idx = 0; // Merker für Zeilenumbruch
DBG_PRINT(" 0x"); // Einleitung der Darstellung
if (myByte < 0x10) // Byte <= 9?
{ DBG_PRINT(0); } // auffüllen
DBG_PRINT(myByte, HEX); // Wert ausgeben
DBG_PRINT(", "); // für Formatierung
if (++idx > 10) // Werte je Zeile erfüllt
{ DBG_PRINTLN(); } // Zeilenumbruch
}
top, code funktioniert perfekt, ja das mit dem delay muß ich mir noch abgewöhnen
Boah...
Schweissperlenabwischgeräusch.
Der teensy hat zum Glück genügend Speicher, dass man nicht zwingend mit jedem byte überlegen muss.
Evtl. kürze ich das später nochmal ein, weil eine Funktion IMHO irgendwie doppelt ist, aber da brauche ich nen Moment für.
Das Alter...
Und ohne Hardware ist das auch nicht gerade ein einfaches Unterfangen.
Na denne. Abarbeiten und dann schau ich zu morgen, dass ich Deine Leds zum Leuchten bekomme.
perfekt,
case 'y':
digitalWrite(LED_BUILTIN, LOW);
break;
case 'x':
digitalWrite(LED_BUILTIN, HIGH);
break;
kann man eigentlich rausnehmen, war nur zum testen, text geht noch nicht ??
da muß ich ja schon uralt sein mit meinen 51, denn soweit mit dem code, wär ich vielleicht schon im rentenalter
Jungspund.
Doch schon.
Zumindest war das angedacht.
Ich seh aber grad das z.B. für "t74" nur ein "." und nicht der Rest dahinter steht.
void sendDriveState(const char *text)
{
static byte checkBit = 0;
if (drive.state != oldDrive.state)
{
sprintf(nextionBuf, "%s%s%s", text, ".txt=", driveStateText[0]);
bool c = bitRead(drive.state, checkBit);
if (c != bitRead(oldDrive.state, checkBit))
{
if (c)
{
sprintf(nextionBuf, "%s%s%s", text, ".txt=", driveStateText[checkBit + 1]);
}
bitSet(oldDrive.state, checkBit);
}
}
if (++checkBit > sizeof(driveStateText) / sizeof(driveStateText[0]))
{ checkBit = 0; }
}
wäre dann die Änderung.
Was sonst noch fehlt musst Du sagen.
ach, oben war das ganze auskommentiert, hab auch neu durchnummeriert, aber kommt noch nix
void pageEight() //status
{
// *INDENT-OFF*
switch (pageElement) {
case 0: sendData("t77", drive.mode, oldDrive.mode); break;
case 1: sendText("t77", drive.mode, oldDrive.mode, driveModeText[drive.mode]); break;
case 2: sendData("t79", drive.state, oldDrive.state); break;
case 3: sendDriveState("t79");break;
case 4: sendDataScaler("n10", drive.voltage, oldDrive.voltage, 255); break;
case 5: sendData("n11", drive.ampere, oldDrive.ampere); break;
case 6: sendDataScaler("n12", drive.rotations, oldDrive.rotations, 255); break;
case 7: sendData("x154", bms.dcdcinW, oldBms.dcdcinW); break;
case 8: sendData("x155", bms.dcdoutA, oldBms.dcdoutA); break;
case 9: sendData("x156", bms.dcdoutV, oldBms.dcdoutV); break;
case 10: sendText("t20", drive.error, oldDrive.error, driveErrorText[drive.error]); break;
default: pageElement = 0xFF; break;
}
und p60 und p61 wird ohne texte eingeblendet, zb.b wenn wahr p60.apk=127, das sind symbole für fehler und balancer
Moment.
Das sind zwei Inhalte, die auf einen Titel gematcht werden?
Ich dachte es geht nur Text oder Wert?
Den Text schau ich nochmal genauer, aber der müsste eigentlich gehen.
Ja, das hatte ich verstanden. Ich hab das auch sauber kommentiert und mich an die Vorgabe gehalten.
Ist das bit gesetzt, wird ein- ist das bit gelöscht wird ausgeblendet.
Kannst ja mal versuchen im setup
batt.oldpack.flag = 0xFF;
zu erzwingen, dass beim ersten Umlauf der Inhalt ausgewertet wird.
ja solange es ein t wie text ist, bei p picture, wird nur ein oder ausgeblendet, zb. beime einblenden p60.aph=0 oder eingeblendet p60.aph=127
stimmt nicht ganz was ich da sag, alle elemente haben die option auf ein und ausblenden, in unserem fall brauchen wir das nur für die symbole
ne ich find den fehler nicht, zeigt noch nix an, und der sound wird wieder bei jedem hochspielen oder reset abgespielt
Ups. Das dürfte aber nicht sein. Es gibt dazu die Variable isActive, die das verhindern soll. Und gesetzt wird die nur im mit den Buchstaben 'g' - 'n'
kann das sein das irgendwas mit page5 ist, da zeigt er auch keine spannung, strom und drehzahl an, udc, idc und speed ?!
Wenn Du die Zeile
//#define DEBUG // Wenn aktiviert, werden Zwischenwerte ausgegeben
einkommentierst, siehst Du evtl. einiges mehr auf dem SerMon.
ja tatsächlich, da kommen die ganzen daten vom page0 auf page8 an
x1.val=22
0xFF,
0x1A,
0xFF,
0xFF,
x143.val=26
0xFF,
0x1A,
0xFF,
0xFF,
0xFF,
n1.val=39
0x1A,
0xFF,
0xFF,
0xFF,
n2.val=44
0x1A,
0xFF,
0xFF,
0xFF,
n3.val=0
0x1A,
0xFF,
0xFF,
t4.val=256
0xFF,
0x1A,
0xFF,
0xFF,
x1.val=22
0xFF,
0x1A,
0xFF,
0xFF,
x143.val=26
x143.val=26 geht auch auf page 9 ein, bei den sounds
jetzt hab ich 3 std gebraucht um zu kapiern, das wir eigentlich das x143 rausgenommen hatten, und gegen das x2 getauscht, dennoch wird es munter weiter auf page8 und page9 gesendet, ich komm einfach nicht drauf
Ich hatte Dir doch geschrieben, dass ich einen Stand von vor 3 Tagen als Grundlage nehme und Du zusehen musst, was danach geändert wurde - oder fragen!?
Du hattest eine 2te Funktion sendCellDiffent1() geschrieben.
Und ich hab die gelöscht und die ursprüngliche so erweitert, dass Du den Parameter übergeben kannst...
Also suche nach x143 und mache daraus x2
ja schon erledigt, vielleicht find ichs noch raus bis morgen warum kein text kommt und ständig die x2 gesendet wird
ach, der text kommt doch im serial, nur ohne "", bestimmt deswegen
isNextionError / errorCounter aktiv?
Ansonsten kannst auch die Differenz vorer mit dem Scaler 10 abgleichen.
Dann gibt es zwei Wege. Entweder die Zeichenketten erweitern, oder beim Zusammenbau. - Ich wäre für letzteres.
//
void sendDriveState(const char *text)
{
static byte checkBit = 0;
if (drive.state != oldDrive.state)
{
sprintf(nextionBuf, "%s%s%s%s", text, ".txt=\"", driveStateText[0]), "\"";
bool c = bitRead(drive.state, checkBit);
if (c != bitRead(oldDrive.state, checkBit))
{
if (c)
{
sprintf(nextionBuf, "%s%s%s%s", text, ".txt=\"", driveStateText[checkBit + 1], "\"");
}
bitSet(oldDrive.state, checkBit);
}
}
if (++checkBit > sizeof(driveStateText) / sizeof(driveStateText[0]))
{ checkBit = 0; }
}
//
void sendText(const char *txt, uint8_t value, uint8_t oldValue, const char *valText)
{
if (value != oldValue)
{
sprintf(nextionBuf, "%s%s%s%s", txt, ".txt=\"", valText, "\"");
sendData2Nextion(nextionBuf);
oldValue = value;
}
}