kWh i ackumulatortankar
Experiment med HC-12 transceivers.
Uppdaterad kod till mottagaren.

Koden har nu en timer som rÀknar sekunder sedan senaste mottagna data.
Shunten Àr nu kalibrerad och visar nu procent istÀllet vÀrde.
Har nu Àndrat frÄn vinter till sommar och stÀngt av en tank.
Temperaraturen Àr nu satt pÄ elpatronen till 65 grader.
Laddningstiden för elpatronen Àr nu satt till kl. 00:00 till 06:00.
Elpatronen schemalÀggs med ett annant system.
Shunten Àr nu kalibrerad och visar nu procent istÀllet vÀrde.
Har nu Àndrat frÄn vinter till sommar och stÀngt av en tank.
Temperaraturen Àr nu satt pÄ elpatronen till 65 grader.
Laddningstiden för elpatronen Àr nu satt till kl. 00:00 till 06:00.
Elpatronen schemalÀggs med ett annant system.
Kalibrera kWh Àr inte klar Ànnu.
Den undre givaren Àr monterad under elpatronen i höjd med returvatten frÄn elementen.
NÀr hela tankens vatten har cirkulerat ett varv kommer den energin att anvÀndas, tror jag.
Om inte elpatronen Àr pÄ förstÄs.
Den undre givaren Àr monterad under elpatronen i höjd med returvatten frÄn elementen.
NÀr hela tankens vatten har cirkulerat ett varv kommer den energin att anvÀndas, tror jag.
Om inte elpatronen Àr pÄ förstÄs.
PlaytformIO, mottagaren Version 2. fil: 'main.cpp'
main.cpp
#include <Arduino.h>
#include <U8g2lib.h>
#include <Arduino_GFX_Library.h>
/*
* MCU Àr en ATmega2560 (Arduino Mega)
* Koden Àr skapad av PchButik.se med hjÀlp av AI
* HC-12 VCC â 5V
* HC-12 GND â GND
* HC-12 TXD â Pin 19 (RX1)
* HC-12 RXD â Pin 18 (TX1)
* HC-12 SET â pin 4 (SET)
* Query-kommandon anvÀnder R-prefix (RC/RP/RB/RF)
* ?-kommandon stöds inte i denna firmwaregren
* AT+RX fungerar alltid och Àr sÀkraste statusdump
*/
/*
* MCU Àr en ATmega2560 (Arduino Mega) Àr pÄ 5 Volt.
* Displayen Àr en 1.8" TFT med ST7735-drivrutin, som vanligtvis krÀver 3.3V logik.
* Glöm inte att anvÀnda nivÄomvandlare för att skydda displayen.
*/
#define BLACK 0x0000
#define WHITE 0xFFFF
#define RED 0xF800
#define GREEN 0x07E0
#define BLUE 0x001F
#define YELLOW 0xFFE0
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define ORANGE 0xFD20
/* ---------- HC12 SERIAL ---------- */
#define HC12 Serial1
//AT-kommandon GET
const char* AT_GET_VERSION = "AT+V";
const char* AT_GET_BAUD = "AT+RB";
const char* AT_GET_CHANNEL = "AT+RC";
const char* AT_GET_POWER = "AT+RP";
const char* AT_GET_RADIO_MODE = "AT+RF";
const char* AT_GET_ALL = "AT+RX";
//Pinnar
#define HC12_SET 4
// Sensor- och displaykonstanter. Justera efter ditt system.
#define WINTER_MODE 0 // 0 = sommar (1 tank) | 1 = vinter (2 tankar)
#define LITER_PER_SENSOR 150.0
#define MIN_TEMP 35.0 //Under denna temperartur rÀknas som ingen anvÀndingsbar energi,
#define MAX_TEMP_VINTER 83.0 //vintertid
#define MAX_TEMP_SOMMAR 65.0 //sommartid
#define WATER_CP 4.186 // kJ/kg°C
#define SHUNT_MIN 224 // 100%
#define SHUNT_MAX 559 // 0%
/* More data bus class: https://github.com/moononournation/Arduino_GFX/wiki/Data-Bus-Class */
Arduino_DataBus *bus = new Arduino_HWSPI(8,10);
Arduino_GFX *gfx = new Arduino_ST7735(bus, 9, 3, false,128, 160, 2, 1, 2, 1,false); //art. 3273 // MÄste anpassas för din skÀrm.
/* ---------- DATA STORAGE ---------- */
float T[5]={0};
int shuntRaw=0;
// D3 anvÀnds som nÀtindikator (1 = nÀt nÀrvarande, 0 = ingen nÀtström)
// Ăvriga anvĂ€nds inte i displayen
// men kan vara anvÀndbara för felsökning eller framtida funktioner.
int D1=0,D2=0,D3=0;
/* ---------- Globals ---------- */
unsigned long lastReceiveTime = 0;
/* ------------------------------Functions--------------------------- */
/* ---------- CRC ---------- */
/**
* @brief BerÀkna en 8-bitars kontrollsumma genom XOR av varje byte i en nullterminerad strÀng.
*
* Itererar över bytes i den angivna null-terminerade C-strÀngen och
* ackumulerar en enkel kontrollsumma genom bitvis XOR av varje byte
* i en 8-bitars ackumulator. Detta Àr en lÀttviktig checksumma (inte
* en formell CRC) och ger grundlÀggande felupptÀckt.
*
* @param data Pekare till en nullterminerad C-strÀng. Bearbetningen
* stoppar vid första NUL ('\0'). För binÀra buffertar som
* kan innehÄlla NUL, anvÀnd en variant som tar en lÀngd.
*
* @return uint8_t Resultatet av 8-bitars XOR-checksumman. Tom strÀng ger 0.
*
* @note Varje char behandlas som en rÄ byte; tecknets signedness hanteras
* genom konvertering till uint8_t vid XOR. Komplextet Àr O(n).
*/
uint8_t calcCRC(const char *data){
uint8_t crc=0;
while(*data) crc ^= *data++;
return crc;
}
/* ---------- PAKETPARSER ---------- */
/**
* parsePacket
*
* Parsar en skrivbar, null-terminerad ASCII-paketstrÀng som innehÄller sensordata/telemetri
* och en avslutande CRC-markör (";CRC=xx"). Om CRC stÀmmer extraheras numeriska vÀrden
* frÄn vÀlkÀnda nycklar och lagras i globala variabler.
*
* Parametrar:
* msg - pekare till en skrivbar, null-terminerad C-strÀng med paketet. Funktionen
* kommer att modifiera bufferten (ersÀtter ';' före CRC med '\0').
*
* Beteende:
* 1. Söker efter substringen ";CRC=" i msg. Om den inte finns returneras funktionen omedelbart.
* 2. LÀser det hexadecimala CRC-vÀrdet som följer ";CRC=" med strtol(...,16) till rxCRC (uint8_t).
* 3. ErsÀtter ';' vid CRC-markören med '\0' för att terminera meddelandedelen som ska CRC-kalkyleras.
* 4. BerÀknar CRC över den trunkerade msg med calcCRC(msg) och jÀmför mot rxCRC.
* Vid avvikelse skrivs "CRC FAIL" till Serial och funktionen returnerar.
* 5. Om CRC matchar söker funktionen i den trunkerade msg efter följande nycklar (strstr):
* - "T1=", "T2=", "T3=", "T4=", "T5=" -> parsas med atof(...) och sparas i T[0..4]
* - "A1=" -> parsas med atoi(...) och sparas i shuntRaw
* - "D1=", "D2=", "D3=" -> parsas med atoi(...) och sparas i D1, D2, D3
* För varje funnen nyckel startar numerisk konvertering direkt efter '='. Om en nyckel
* saknas Àndras den inte.
*
* Biverkningar / globala som Àndras:
* - Ăverskriver *crcPos med '\0' (modifierar msg-bufferten).
* - Kan skriva "CRC FAIL" till Serial vid CRC-missmatch.
* - Uppdaterar globala: T[] (0..4), shuntRaw, D1, D2, D3.
*
* FörutsÀttningar / noteringar:
* - msg mÄste vara skrivbar (funktionen skriver en NUL).
* - msg mÄste vara null-terminerad.
* - CRC-vÀrdet mÄste vara hexadecimalt; strtol accepterar bÄde "0x" och rena hex-siffror.
* - calcCRC(msg) förvÀntas vara kompatibel med det insÀnda vÀrdet.
* - atof/atoi anvÀnds för konvertering:
* - atof accepterar decimalpunkt '.' för brÄkdel.
* - atoi/atof returnerar 0 vid parse-fel eller om vÀrdet Àr noll.
*
* Exempelpaket:
* "T1=23.5;T2=19.0;A1=1023;D1=1;D2=0;CRC=AB"
*
* Felhantering:
* - Om ";CRC=" saknas eller CRC inte matchar returnerar funktionen tidigt och Àndrar inte
* de numeriska globala (förutom modifiering av msg-bufferten).
*/
void parsePacket(char *msg){
char *crcPos = strstr(msg,";CRC=");
if(!crcPos) return;
uint8_t rxCRC = strtol(crcPos+5,NULL,16);
*crcPos=0;
if(calcCRC(msg)!=rxCRC){
Serial.println("CRC FAIL");
return;
}
char *p;
if((p=strstr(msg,"T1="))) T[0]=atof(p+3);
if((p=strstr(msg,"T2="))) T[1]=atof(p+3);
if((p=strstr(msg,"T3="))) T[2]=atof(p+3);
if((p=strstr(msg,"T4="))) T[3]=atof(p+3);
if((p=strstr(msg,"T5="))) T[4]=atof(p+3);
if((p=strstr(msg,"A1="))) shuntRaw=atoi(p+3);
if((p=strstr(msg,"D1="))) D1=atoi(p+3);
if((p=strstr(msg,"D2="))) D2=atoi(p+3);
if((p=strstr(msg,"D3="))) D3=atoi(p+3);
}
/* ---------- DRAW BAR ---------- */
/**
* @brief Ritar en horisontell progress-/fyllningsstapel med vit kant och fÀrgad inre del.
*
* @param x VÀnster pixelkoordinat för ytterrektangeln.
* @param y Ăvre pixelkoordinat för ytterrektangeln.
* @param w Bredd i pixlar för ytterrektangeln (bör vara >= 2 för inre yta).
* @param h Höjd i pixlar för ytterrektangeln (bör vara >= 2).
* @param value Aktuellt vÀrde som ska representeras; mappas proportionellt till fyllbredden.
* @param max VÀrdet som motsvarar fullt fylld stapel (mÄste vara > 0 för korrekt beteende).
* @param col 16-bit fÀrg som anvÀnds för fyllningen.
*
* Beteende:
* - Ritar en vit ytterrektangel pÄ (x, y) med storlek (w, h).
* - BerÀknar inre fyllbredd med map(value, 0, max, 0, w - 2).
* - Fyller den inre rektangeln pÄ (x+1, y+1) med bredd = fill och höjd = h - 2 i fÀrgen col.
*
* Noteringar / förutsÀttningar:
* - Funktionen förutsÀtter att en global pekare `gfx` och fÀrgkonstanten WHITE finns.
* - För att undvika extrapolering bör caller klippa value till intervallet [0, max].
* - Om w < 2 eller h < 2 blir inre yta <= 0; beteendet Àr ej definierat.
* - Om max == 0 blir mappningen felaktig; sÀkerstÀll max > 0 innan anrop.
*/
void drawBar(int x,int y,int w,int h,int value,int max,uint16_t col){
gfx->drawRect(x,y,w,h,WHITE);
int fill = map(value,0,max,0,w-2);
gfx->fillRect(x+1,y+1,fill,h-2,col);
}
/**
* @brief Returnerar maxtemperaturen som gÀller för aktuell sÀsong.
*
* Funktion vÀljer mellan MAX_TEMP_VINTER och MAX_TEMP_SOMMAR beroende pÄ
* den globala flaggan WINTER_MODE (icke-noll = vinter).
*
* @return float Maxtemperatur i samma enhet som MAX_TEMP_VINTER/MAX_TEMP_SOMMAR.
*/
float getMaxTemp(){
return WINTER_MODE ? MAX_TEMP_VINTER : MAX_TEMP_SOMMAR;
}
/**
* Konverterar ett rÄtt shuntvÀrde till en procentuell nivÄ (0.0 - 100.0).
*
* - VĂ€rdet klipps till intervallet [SHUNT_MIN, SHUNT_MAX].
* - SHUNT_MIN motsvarar 100% och SHUNT_MAX motsvarar 0%.
* - Skyddar mot division med noll om SHUNT_MAX == SHUNT_MIN.
*
* @param raw RÄtt shuntvÀrde (t.ex. ADC-vÀrde).
* @return Flyttal i intervallet [0.0, 100.0].
*/
float calcShuntPercent(int raw) {
if (SHUNT_MAX == SHUNT_MIN) return 0.0f; // skydd mot division med noll
if (raw < SHUNT_MIN) raw = SHUNT_MIN;
else if (raw > SHUNT_MAX) raw = SHUNT_MAX;
float denom = (float)(SHUNT_MAX - SHUNT_MIN);
float percent = (float)(SHUNT_MAX - raw) * 100.0f / denom;
if (percent < 0.0f) percent = 0.0f;
else if (percent > 100.0f) percent = 100.0f;
return percent;
}
/**
* BerÀknar energin som krÀvs för att vÀrma vattenmÀngden kopplad till en sensor
* frÄn MIN_TEMP upp till den angivna temperaturen.
*
* - Temperatur klipps till intervallet [MIN_TEMP, getMaxTemp()].
* - Temperaturdifferensen delta = clamped_temp - MIN_TEMP.
* - Antal tankar bestÀms av WINTER_MODE (2.0 vid vinter, annars 1.0).
* - Massan i kg berĂ€knas som: mass = LITER_PER_SENSOR * tanks (1 L â 1 kg).
* - VĂ€rme i kJ: kJ = mass * WATER_CP * delta
* - Returneras i kWh: return kJ / 3600.0
*
* @param temp MÄltemperatur i °C.
* @return Energi i kilowattimmar (kWh) som krÀvs för uppvÀrmning frÄn MIN_TEMP.
*
* Notera:
* - Om temp <= MIN_TEMP returneras 0 (ingen energi behövs).
* - Funktionen anvÀnder globala konstanter: MIN_TEMP, LITER_PER_SENSOR, WATER_CP,
* WINTER_MODE och getMaxTemp().
*/
float calcEnergy(float temp){
float maxTemp = getMaxTemp();
if(temp < MIN_TEMP) temp = MIN_TEMP;
if(temp > maxTemp) temp = maxTemp;
float delta = temp - MIN_TEMP;
float tanks = WINTER_MODE ? 2.0 : 1.0;
float mass = LITER_PER_SENSOR * tanks; // kg (1L â 1kg)
float kJ = mass * WATER_CP * delta;
return kJ / 3600.0;
}
/**
* drawTimer()
*
* Uppdaterar ett litet omrÄde pÄ displayen för att visa tid sedan sista
* mottagna hÀndelse, eller en timeout-meddelande om ingen ny signal finns.
*
* Beteende:
* - BerÀknar förfluten tid i sekunder som (millis() - lastReceiveTime) / 1000.
* - Rensar en fast rektangel (0, 68, 75, 10) genom att fylla med BLACK.
* - SĂ€tter textkursorn till (0, 69).
* - Om förfluten tid >= 99: sÀtter textfÀrg till RED och skriver "No signal!".
* - Annars: sÀtter textfÀrg till WHITE och skriver "Last RX: <elapsed>s".
*
* Antaganden och sidoeffekter:
* - gfx Àr en pekare/objekt för display som erbjuder:
* fillRect(x, y, w, h, color), setCursor(x, y), setTextColor(color),
* och print(...) metoder.
* - FÀrgkonstanterna BLACK, RED och WHITE Àr definierade och förstÄs av gfx.
* - lastReceiveTime och millis() Àr unsigned long tidsstÀmplar i ms.
* - Funktionen returnerar inget; den uppdaterar endast displayen.
*
* Noteringar:
* - Heltalsdivision anvÀnds för att fÄ hela sekunder; sub-sekunders precision försvinner.
* - TimeoutgrÀnsen (99 s), rektangelns koordinater och texter Àr hÄrdkodade.
* - ĂvervĂ€g att klippa eller formatera vĂ€rdet för att undvika layoutproblem vid mycket stora tal.
*/
void drawTimer(){
unsigned long elapsed = (millis() - lastReceiveTime)/1000;
// rensa bara timer-ytan
gfx->fillRect(0,68,75,11,BLACK);
gfx->setCursor(0,69);
// röd text om timeout
if(elapsed >= 99){
gfx->setTextColor(RED);
gfx->print("No signal!");
} else {
gfx->setTextColor(WHITE);
gfx->print("Last RX: ");
gfx->print(elapsed);
gfx->print("s");
}
}
/**
* @brief Ritar huvudstatusskÀrmen pÄ gfx-displayen.
*
* Denna funktion tömmer skÀrmen och ritar en sammanfattning av tanktemperaturer,
* per-sensor och total lagrad energi, en progressbar för total energi vs berÀknad
* maxkapacitet, shuntprocent och en nÀtindikator (El).
*
* Placering / beteende:
* - Rensar skÀrmen och sÀtter textfÀrg till WHITE.
* - Skriver rubriken "Tank:" uppe till vÀnster (kursorn 0,0).
* - Itererar över 5 temperatursensorer (T[0]..T[4]):
* - BerÀknar per-sensor-energi med calcEnergy(T[i]) och summerar totalEnergy.
* - Adderar maxPerSensor till maxTotal (maxPerSensor berÀknas frÄn massan,
* WATER_CP och temperaturskillnaden).
* - Skriver varje sensors temperatur (1 decimal) och energi (1 decimal)
* som "XX.XC YY.YkWh" pÄ y-positions startande vid y=12, med 20 px mellanrum.
* - Ritar en röd stapel till höger (drawBar vid x=80) som visar temperaturen
* mappad till 0..100 (klippt).
* - BerÀknar percent = (totalEnergy / maxTotal) * 100 (0 om maxTotal == 0).
* - Skriver TOTAL: med totalEnergy (1 decimal, kWh) och percent (0 decimaler)
* pÄ y=105 och ritar en grön progressbar över x=0..158 vid y=115 som visar percent
* (klippt till [0,100]).
* - BerÀknar och skriver shuntprocent (calcShuntPercent(shuntRaw)) vid y=80.
* - Ritar nÀtindikatorn "El:" vid y=93 och en cirkel vid (30,95) fylld med WHITE
* nÀr D3 Àr sann (nÀt nÀrvarande) eller RED nÀr D3 Àr falsk.
*
* Enheter och berÀkningar:
* - tanks = WINTER_MODE ? 2 : 1
* - mass = LITER_PER_SENSOR * tanks (liter â kg)
* - maxPerSensor = (mass * WATER_CP * (maxTemp - MIN_TEMP)) / 3600.0f
* - Antas att WATER_CP Àr i kJ/(kg*K) sÄ delning med 3600 ger kWh per sensor,
* i enlighet med calcEnergy().
*
* Formatering och klippning:
* - Temperaturer skrivs med en decimal.
* - Energier skrivs med en decimal och mÀrks "kWh".
* - Procent skrivs utan decimaler.
* - TemperatuvÀrden för varje stapel klipps till [0,100] via constrain().
* - Total percent klipps till [0,100] innan visning.
*
* Beroenden / anvÀnda globala:
* - gfx (fillScreen, setTextColor, setCursor, print/println, fillCircle)
* - Konstanter/flaggor: BLACK, WHITE, RED, GREEN, WINTER_MODE, LITER_PER_SENSOR,
* WATER_CP, MIN_TEMP
* - Funktioner: getMaxTemp(), calcEnergy(float), drawBar(...), calcShuntPercent(...)
* - Globala vÀrden: float T[5], shuntRaw, D3
*
* Bieffekter:
* - Uppdaterar displayen via gfx-anrop.
* - LÀser flera globala variabler och hjÀlpfunktioner.
*
* Noteringar:
* - Funktionen anvÀnder alltid 5 sensorer och fasta layout-koordinater.
* - Se till att calcEnergy() och WATER_CP anvÀnder enhet som ger kWh för meningsfulla procentsatser.
*
* Komplextet:
* - Tidskomplexitet O(N) för N = 5 (fast).
*
* Returnerar:
* - void (renderar direkt till displayen).
*/
void drawScreen(){
gfx->fillScreen(BLACK);
gfx->setTextColor(WHITE);
gfx->setCursor(0,0);
gfx->println("Tank:");
float totalEnergy = 0.0f;
float maxTotal = 0.0f;
float tanks = WINTER_MODE ? 2.0f : 1.0f;
float mass = LITER_PER_SENSOR * tanks;
float maxTemp = getMaxTemp();
float maxPerSensor = (mass * WATER_CP * (maxTemp - MIN_TEMP)) / 3600.0f;
// Temperatures + per-sensor energy
for(int i = 0; i < 5; ++i){
float e = calcEnergy(T[i]);
totalEnergy += e;
maxTotal += maxPerSensor;
// Text line
gfx->setCursor(0, 12 + i * 10);
gfx->print(T[i], 1);
gfx->print("C ");
gfx->print(e, 1);
gfx->println("kWh");
// Temperatur till stapel: mappa temperatur till 0-100 för visning
int tempVis = (int)constrain(T[i], 0.0f, 100.0f);
drawBar(80, i * 20 + 2, 80, 12, tempVis, 100, RED);
}
// Total energy och procent
float percent = (maxTotal > 0.0f) ? (totalEnergy / maxTotal) * 100.0f : 0.0f;
gfx->setCursor(0, 105);
gfx->print("TOTAL:");
gfx->print(totalEnergy, 1);
gfx->print("kWh ");
gfx->print(percent, 0);
gfx->println("%");
drawBar(0, 115, 158, 12, (int)constrain(percent, 0.0f, 100.0f), 100, GREEN);
// Shunt
float shuntPercent = calcShuntPercent(shuntRaw);
gfx->setCursor(0, 80);
gfx->print("Shunt:");
gfx->print(shuntPercent, 0);
gfx->println("%");
// Digital / mains indicator
gfx->setCursor(0, 93);
gfx->print("El:");
gfx->fillCircle(30, 95, 5, D3 ? WHITE : RED);
}
/**
* ---------- SETUP ---------- *
*
* @brief Initierar hÄrdvaruperiferier och förbereder mottagarens UI.
*
* Körs en gÄng vid programstart. Utför följande:
* - Initierar hÄrdvaru-Serial för debug pÄ 9600 baud.
* - Initierar HC12-radio pÄ 9600 baud.
* - Initierar displayobjektet, rensar skÀrmen till BLACK och sÀtter textstorlek till 1.
* - Skriver "Receiver ready" till Serial-konsolen.
*
* @note Antas att globala objekt/handtag Serial, HC12 och gfx Àr korrekt konstruerade
* och tillgÀngliga innan denna funktion anropas.
* @pre Serial, HC12 och gfx mÄste vara giltiga och deras begin()-metoder anropbara.
* @post Serial och HC12 Àr konfigurerade till 9600 baud. Displayen Àr rensad och klar för text.
*/
void setup(){
Serial.begin(9600);
HC12.begin(9600);
gfx->begin();
gfx->fillScreen(BLACK);
gfx->setTextSize(1);
Serial.println("Receiver ready");
}
/* ---------- LOOP ---------- */
/**
* @brief Arduino loop-hanterare: ta emot, parsa och validera paket frÄn HC12-radio.
*
* LÀs kontinuerligt bytes frÄn HC12-serien och bygg paket avgrÀnsade med
* '<' (start) och '>' (slut) i en statisk teckenbuffert. Vid slutavgrÀnsare
* terminera paketet, skriv ut det till Serial, gör en kopia, anropa parsePacket(...)
* och jÀmför buffern med kopian för att avgöra om paketet klarade CRC/validering.
* SkÀrmen uppdateras med drawScreen() efter varje paket.
*
* Beteende:
* - AnvÀnder static char buf[180] och static byte idx för ackumulering över anrop.
* - '<' ÄterstÀller index för att pÄbörja nytt paket.
* - '>' terminera buffern, skriv ut, kopiera, anropa parsePacket(buf), jÀmför med kopian
* och skriv "CRC OK" eller "CRC ERROR". Anropa drawScreen() och nollstÀll idx.
* - Ăvriga tecken lĂ€ggs till bufferten om idx < 179 (plats för avslutande NUL).
*
* Antaganden och begrÀnsningar:
* - HC12 implementerar available() och read() (Stream-liknande).
* - Valideringslogiken förutsĂ€tter att parsePacket muterar buf vid framgĂ„ng; detta Ă€r skört â
* bÀttre Àr att parsePacket returnerar explicit status.
* - Ingen timeout eller utförlig hantering av för lÄnga paket utöver buffertstorleken.
* - Payload som innehÄller '<' eller '>' bryter framer eftersom inga escape-mekanismer finns.
* - Ăverflöd (index >=179) ignoreras i dagslĂ€get utan felrapportering.
*
* FörbÀttringsförslag:
* - LÄt parsePacket returnera success/failure i stÀllet för att jÀmföra buffrar.
* - LÀgg till paket-timeout och explicit överflödes-/felhantering.
* - Implementera en enkel state-machine som stödjer escaping om payloads kan innehÄlla
* avgrÀnsartecken.
* - AnvÀnd lÀngdbaserade och bounds-kontrollerade API:er och undvik tysta truncations.
*
* @return void
*/
void loop(){
static char buf[180];
static byte idx=0;
static unsigned long lastDraw = 0; // <-- NY
while(HC12.available()){
char c = HC12.read();
if(c=='<') idx=0;
else if(c=='>'){
buf[idx]=0;
lastReceiveTime = millis(); // nollstÀll timer
Serial.print("RAW: ");
Serial.println(buf);
char test[180];
strcpy(test,buf);
parsePacket(buf);
if(strcmp(buf,test)==0)
Serial.println("CRC ERROR");
else
Serial.println("CRC OK");
drawScreen(); // direkt uppdatering vid paket
idx=0;
}
else if(idx<179){
buf[idx++]=c;
}
}
// <-- NY PERIODISK SKĂRMUPPDATERING
if(millis() - lastDraw >= 1000){
lastDraw = millis();
drawTimer(); // bara timer ritas om
}
}
