Tips: IKEA SPARSNÄS Energidisplay

Elektronikrelaterade (på komponentnivå) frågor och funderingar.
Användarvisningsbild
swapper
Inlägg: 1075
Blev medlem: 14 augusti 2012, 23:18:15
Ort: Helsingborg

Re: Tips: IKEA SPARSNÄS Energidisplay

Inlägg av swapper »

Köpte 2 st på IKEA i Helsingborg för 99:- st

Någon som testat
https://github.com/strigeus/sparsnas_decoder

Har beställt RTL2832U SDR mottagare för att testa.
larky
Inlägg: 1095
Blev medlem: 7 mars 2007, 07:54:18
Ort: Skellefteå
Kontakt:

Re: Tips: IKEA SPARSNÄS Energidisplay

Inlägg av larky »

Testat men fick det inte att fungera.
Det är "strigeus" här på forumet som skrivit dekodern, och han önskade köpa ett par Sparsnäs på förra sidan i den här tråden.
Användarvisningsbild
GastonDeVille
EF Sponsor
Inlägg: 1976
Blev medlem: 24 oktober 2013, 20:36:07
Ort: Småland - lite utanför Gränna

Re: Tips: IKEA SPARSNÄS Energidisplay

Inlägg av GastonDeVille »

Jodå, sparsnäs-burken kom fram välbehållen och funkar hos brorsan trots nästan 100 m mellan uthuset och bostaden med halvbra fri sikt. Ska bli intressant om den överlever kommande åskväder med diverse urladdningar.
Användarvisningsbild
sommarlov
Inlägg: 514
Blev medlem: 28 november 2015, 19:03:40
Ort: 08

Re: Tips: IKEA SPARSNÄS Energidisplay

Inlägg av sommarlov »

Jag skrev i den andra Sparsnäs tråden, men det finns Sparsnäsar att köpa på Ikea Kungens Kurva för 199 kr. Köpte igår.
Användarvisningsbild
sommarlov
Inlägg: 514
Blev medlem: 28 november 2015, 19:03:40
Ort: 08

Re: Tips: IKEA SPARSNÄS Energidisplay

Inlägg av sommarlov »

Jag har byggt en liten enkel krets med en ESP8266 och en RFM69 radio som lyssnar på sändaren. Denna data loggar jag sedan i OpenHab via Mqtt (men det går jag inte in på här).

Koden nedan går nog inte att kompilera rakt av för er för jag har en del bibliotek jag inte bifogar, men ingen av dem behövs för basfunktionen dvs ta emot datat. Jag använde mig av https://github.com/strigeus/sparsnas_decoder (Tack!) för själva protokollet.

Kod: Markera allt


#include "RFM69Registers.h"
#include <Arduino.h>
#include <SPI.h>

#define RF69_MODE_SLEEP 0      // XTAL OFF
#define RF69_MODE_STANDBY 1    // XTAL ON
#define RF69_MODE_SYNTH 2      // PLL ON
#define RF69_MODE_RX 3	 // RX MODE
#define RF69_MODE_TX 4	 // TX MODE

#define SENSOR_ID xxxxxx <- ange din 6 siffriga kod här, ta sista 6 siffrorna av 400 666 111. Koden finns i sändaren under batteriet
#define PULSES_PER_KWH 1000 <- samma här, står på elmätaren

uint32_t FXOSC = 32000000;
uint32_t TwoPowerToNinteen = 524288; // 2^19
float RF69_FSTEP = (1.0 * FXOSC) / TwoPowerToNinteen; // p13 in datasheet
uint32_t FREQUENCY = 868000000;
uint16_t BITRATE = FXOSC / 40000; // 40kBps
uint16_t FREQUENCYDEVIATION = 10000 / RF69_FSTEP; // 10kHz
uint16_t SYNCVALUE = 0xd201;
uint8_t RSSITHRESHOLD = 210; // must be set to dBm = (-Sensitivity / 2), default is 0xE4 = 228 so -114dBm
uint8_t PAYLOADLENGTH = 20;

#define _interruptNum 5

static volatile uint8_t DATA[21];
static volatile uint8_t TEMPDATA[21];
static volatile uint8_t DATALEN;
static volatile uint16_t RSSI; // Most accurate RSSI during reception (closest to the reception)
static volatile uint8_t _mode;
static volatile bool inInterrupt = false; // Fake Mutex
uint8_t enc_key[5];

unsigned long lastCheckedForUpdate = millis();
unsigned long lastRecievedData = millis();

// ----------------------------------------------------

void setup() {
  Serial.begin(115200);

  // Calc encryption key, used for bytes 5-17
  const uint32_t sensor_id_sub = SENSOR_ID - 0x5D38E8CB;
  enc_key[0] = (uint8_t)(sensor_id_sub >> 24);
  enc_key[1] = (uint8_t)(sensor_id_sub);
  enc_key[2] = (uint8_t)(sensor_id_sub >> 8);
  enc_key[3] = 0x47;
  enc_key[4] = (uint8_t)(sensor_id_sub >> 16);

  if (!initialize(FREQUENCY)) {
    Serial.println("Unable to initialize the radio. Exiting.");
    while (1) {
      yield();
    }
  }
  Serial.println("Listening on " + String(getFrequency()) + "hz.");

  Serial.println("Done in setup.");
}

bool initialize(uint32_t frequency) {
  frequency = frequency / RF69_FSTEP;

  const uint8_t CONFIG[][2] = {
    /* 0x01 */ {REG_OPMODE, RF_OPMODE_SEQUENCER_ON | RF_OPMODE_LISTEN_OFF | RF_OPMODE_STANDBY},
    /* 0x02 */ {REG_DATAMODUL, RF_DATAMODUL_DATAMODE_PACKET | RF_DATAMODUL_MODULATIONTYPE_FSK | RF_DATAMODUL_MODULATIONSHAPING_01},
    /* 0x03 */ {REG_BITRATEMSB, (uint8_t)(BITRATE >> 8)},
    /* 0x04 */ {REG_BITRATELSB, (uint8_t)(BITRATE)},
    /* 0x05 */ {REG_FDEVMSB, (uint8_t)(FREQUENCYDEVIATION >> 8)},
    /* 0x06 */ {REG_FDEVLSB, (uint8_t)(FREQUENCYDEVIATION)},
    /* 0x07 */ {REG_FRFMSB, (uint8_t)(frequency >> 16)},
    /* 0x08 */ {REG_FRFMID, (uint8_t)(frequency >> 8)},
    /* 0x09 */ {REG_FRFLSB, (uint8_t)(frequency)},
    /* 0x19 */ {REG_RXBW, RF_RXBW_DCCFREQ_010 | RF_RXBW_MANT_16 | RF_RXBW_EXP_4}, // p26 in datasheet, filters out noise 
    /* 0x25 */ {REG_DIOMAPPING1, RF_DIOMAPPING1_DIO0_01},			     // PayloadReady
    /* 0x26 */ {REG_DIOMAPPING2, RF_DIOMAPPING2_CLKOUT_OFF},			 // DIO5 ClkOut disable for power saving
    /* 0x28 */ {REG_IRQFLAGS2, RF_IRQFLAGS2_FIFOOVERRUN},			     // writing to this bit ensures that the FIFO & status flags are reset
    /* 0x29 */ {REG_RSSITHRESH, RSSITHRESHOLD},					     
    /* 0x2D */ {REG_PREAMBLELSB, 3}, // default 3 preamble bytes 0xAAAAAA
    /* 0x2E */ {REG_SYNCCONFIG, RF_SYNC_ON | RF_SYNC_FIFOFILL_AUTO | RF_SYNC_SIZE_2 | RF_SYNC_TOL_0},
    /* 0x2F */ {REG_SYNCVALUE1, (uint8_t)(SYNCVALUE >> 8)},
    /* 0x30 */ {REG_SYNCVALUE2, (uint8_t)(SYNCVALUE)},
    /* 0x37 */ {REG_PACKETCONFIG1, RF_PACKET1_FORMAT_FIXED | RF_PACKET1_DCFREE_OFF | RF_PACKET1_CRC_OFF | RF_PACKET1_CRCAUTOCLEAR_ON | RF_PACKET1_ADRSFILTERING_OFF},
    /* 0x38 */ {REG_PAYLOADLENGTH, PAYLOADLENGTH},
    /* 0x3C */ {REG_FIFOTHRESH, RF_FIFOTHRESH_TXSTART_FIFONOTEMPTY | RF_FIFOTHRESH_VALUE},				   // TX on FIFO not empty
    /* 0x3D */ {REG_PACKETCONFIG2, RF_PACKET2_RXRESTARTDELAY_2BITS | RF_PACKET2_AUTORXRESTART_ON | RF_PACKET2_AES_OFF}, // RXRESTARTDELAY must match transmitter PA ramp-down time (bitrate dependent)
    /* 0x6F */ {REG_TESTDAGC, RF_DAGC_IMPROVED_LOWBETA0}, // run DAGC continuously in RX mode for Fading Margin Improvement, recommended default for AfcLowBetaOn=0
    {255, 0}
  };

  digitalWrite(SS, HIGH);
  pinMode(SS, OUTPUT);
  SPI.begin();
  SPI.setDataMode(SPI_MODE0);
  SPI.setBitOrder(MSBFIRST);
  // decided to slow down from DIV2 after SPI stalling in some instances,
  // especially visible on mega1284p when RFM69 and FLASH chip both present
  SPI.setClockDivider(SPI_CLOCK_DIV4);

  unsigned long start = millis();
  uint8_t timeout = 50;
  do {
    writeReg(REG_SYNCVALUE1, 0xAA);
    yield();
  } while (readReg(REG_SYNCVALUE1) != 0xaa && millis() - start < timeout);
  start = millis();
  do {
    writeReg(REG_SYNCVALUE1, 0x55);
    yield();
  } while (readReg(REG_SYNCVALUE1) != 0x55 && millis() - start < timeout);

  for (uint8_t i = 0; CONFIG[i][0] != 255; i++) {
    writeReg(CONFIG[i][0], CONFIG[i][1]);
    yield();
  }

  setMode(RF69_MODE_STANDBY);
  start = millis();
  while (((readReg(REG_IRQFLAGS1) & RF_IRQFLAGS1_MODEREADY) == 0x00) && millis() - start < timeout) {
    // wait for ModeReady
    codeLibrary.wait(1);
  }
  if (millis() - start >= timeout) {
    Serial.println("Failed on waiting for ModeReady()");
    return false;
  }
  attachInterrupt(_interruptNum, interruptHandler, RISING);

  return true;
}

uint32_t getFrequency() {
  return RF69_FSTEP * (((uint32_t)readReg(REG_FRFMSB) << 16) + ((uint16_t)readReg(REG_FRFMID) << 8) + readReg(REG_FRFLSB));
}

void receiveBegin() {
  DATALEN = 0;
  RSSI = 0;
  if (readReg(REG_IRQFLAGS2) & RF_IRQFLAGS2_PAYLOADREADY) {
    uint8_t val = readReg(REG_PACKETCONFIG2);
    // avoid RX deadlocks
    writeReg(REG_PACKETCONFIG2, (val & 0xFB) | RF_PACKET2_RXRESTART);
  }
  setMode(RF69_MODE_RX);
}

// checks if a packet was received and/or puts transceiver in receive (ie RX or listen) mode
bool receiveDone() {
  // noInterrupts(); // re-enabled in unselect() via setMode() or via
  // receiveBegin()

  if (_mode == RF69_MODE_RX && DATALEN > 0) {
    setMode(RF69_MODE_STANDBY); // enables interrupts
    return true;

  } else if (_mode == RF69_MODE_RX) {
    // already in RX no payload yet
    // interrupts(); // explicitly re-enable interrupts
    return false;
  }

  receiveBegin();
  return false;
}

// get the received signal strength indicator (RSSI)
uint16_t readRSSI(bool forceTrigger = false) {
  uint16_t rssi = 0;
  if (forceTrigger) {
    // RSSI trigger not needed if DAGC is in continuous mode
    writeReg(REG_RSSICONFIG, RF_RSSI_START);
    while ((readReg(REG_RSSICONFIG) & RF_RSSI_DONE) == 0x00) {
      // wait for RSSI_Ready
      yield();
    }
  }
  rssi = -readReg(REG_RSSIVALUE);
  rssi >>= 1;
  return rssi;
}

uint8_t readReg(uint8_t addr) {
  select();
  SPI.transfer(addr & 0x7F);
  uint8_t regval = SPI.transfer(0);
  unselect();
  return regval;
}

void writeReg(uint8_t addr, uint8_t value) {
  select();
  SPI.transfer(addr | 0x80);
  SPI.transfer(value);
  unselect();
}

// select the RFM69 transceiver (save SPI settings, set CS low)
void select() {
  // noInterrupts();
  digitalWrite(SS, LOW);
}

// unselect the RFM69 transceiver (set CS high, restore SPI settings)
void unselect() {
  digitalWrite(SS, HIGH);
  // interrupts();
}

void interruptHandler() {
  if (inInterrupt) {
    //Serial.println("Already in interruptHandler.");
    return;
  }
  inInterrupt = true;

  if (_mode == RF69_MODE_RX && (readReg(REG_IRQFLAGS2) & RF_IRQFLAGS2_PAYLOADREADY)) {
    setMode(RF69_MODE_STANDBY);
    DATALEN = 0;
    select();

    // Init reading
    SPI.transfer(REG_FIFO & 0x7F);

    // Read 20 bytes
    for (uint8_t i = 0; i < 20; i++) {
      TEMPDATA[i] = SPI.transfer(0);
    }

    // CRC is done BEFORE decrypting message
    uint16_t crc = crc16(TEMPDATA, 18);
    uint16_t packet_crc = TEMPDATA[18] << 8 | TEMPDATA[19];

    // Decrypt message
    for (size_t i = 0; i < 13; i++) {
      TEMPDATA[5 + i] = TEMPDATA[5 + i] ^ enc_key[i % 5];
    }

    uint32_t rcv_sensor_id = TEMPDATA[5] << 24 | TEMPDATA[6] << 16 | TEMPDATA[7] << 8 | TEMPDATA[8];

    String output;

    if (TEMPDATA[0] != 0x11 || TEMPDATA[1] != (SENSOR_ID & 0xFF) || TEMPDATA[3] != 0x07 || TEMPDATA[4] != 0x0E || rcv_sensor_id != SENSOR_ID) {
      /*
      output = "Bad package: ";
      for (int i = 0; i < 18; i++) {
        output += codeLibrary.ToHex(TEMPDATA[i]) + " ";
      }
      Serial.println(output);
      */

    } else {
      /*
        0: uint8_t length;        // Always 0x11
        1: uint8_t sender_id_lo;  // Lowest byte of sender ID
        2: uint8_t unknown;       // Not sure
        3: uint8_t major_version; // Always 0x07 - the major version number of the sender.
        4: uint8_t minor_version; // Always 0x0E - the minor version number of the sender.
        5: uint32_t sender_id;    // ID of sender
        9: uint16_t time;         // Time in units of 15 seconds.
        11:uint16_t effect;       // Current effect usage
        13:uint32_t pulses;       // Total number of pulses
        17:uint8_t battery;       // Battery level, 0-100.
      */

      output = "";
      for (uint8_t i = 0; i < 20; i++) {
        output += codeLibrary.ToHex(TEMPDATA[i]) + " ";
      }
      Serial.println(output);

      int seq = (TEMPDATA[9] << 8 | TEMPDATA[10]);
      int effect = (TEMPDATA[11] << 8 | TEMPDATA[12]);
      int pulse = (TEMPDATA[13] << 24 | TEMPDATA[14] << 16 | TEMPDATA[15] << 8 | TEMPDATA[16]);
      int battery = TEMPDATA[17];
      float watt =  (float)((3600000 / PULSES_PER_KWH) * 1024) / (effect);
      output = "Seq " + String(seq) + ": ";
      output += String(watt) + " W, total: ";
      output += String(pulse / 1000) + " kWh, battery ";
      output += String(battery) + "% ";

      output += (crc == packet_crc ? "" : "CRC ERR");
      Serial.println(output);
    }

    unselect();
    setMode(RF69_MODE_RX);
  }
  RSSI = readRSSI();

  inInterrupt = false;
}

void setMode(uint8_t newMode) {
  if (newMode == _mode) {
    return;
  }

  uint8_t val = readReg(REG_OPMODE);
  switch (newMode) {
    case RF69_MODE_TX:
      writeReg(REG_OPMODE, (val & 0xE3) | RF_OPMODE_TRANSMITTER);
      break;
    case RF69_MODE_RX:
      writeReg(REG_OPMODE, (val & 0xE3) | RF_OPMODE_RECEIVER);
      break;
    case RF69_MODE_SYNTH:
      writeReg(REG_OPMODE, (val & 0xE3) | RF_OPMODE_SYNTHESIZER);
      break;
    case RF69_MODE_STANDBY:
      writeReg(REG_OPMODE, (val & 0xE3) | RF_OPMODE_STANDBY);
      break;
    case RF69_MODE_SLEEP:
      writeReg(REG_OPMODE, (val & 0xE3) | RF_OPMODE_SLEEP);
      break;
    default:
      return;
  }

  // we are using packet mode, so this check is not really needed but waiting for mode ready is necessary when
  // going from sleep because the FIFO may not be immediately available from previous mode.
  unsigned long start = millis();
  uint16_t timeout = 500;
  while (_mode == RF69_MODE_SLEEP && millis() - start < timeout && (readReg(REG_IRQFLAGS1) & RF_IRQFLAGS1_MODEREADY) == 0x00) {
    // wait for ModeReady
    yield();
  }
  if (millis() - start >= timeout) {
      //Timeout when waiting for getting out of sleep
  }

  _mode = newMode;
}


uint16_t crc16(volatile uint8_t *data, size_t n) {
  uint16_t crcReg = 0xffff;
  size_t i, j;
  for (j = 0; j < n; j++) {
    uint8_t crcData = data[j];
    for (i = 0; i < 8; i++) {
      if (((crcReg & 0x8000) >> 8) ^ (crcData & 0x80))
        crcReg = (crcReg << 1) ^ 0x8005;
      else
        crcReg = (crcReg << 1);
      crcData <<= 1;
    }
  }
  return crcReg;
}

void loop() {
  if (receiveDone()) {
    lastRecievedData = millis();
    // Send data to Mqtt server
    Serial.println("We got data to send.");
    // Wait a bit
    codeLibrary.wait(500);
  }

}
Du har inte behörighet att öppna de filer som bifogats till detta inlägg.
Användarvisningsbild
adent
Inlägg: 4094
Blev medlem: 27 november 2008, 22:56:23
Ort: Utanför Jönköping
Kontakt:

Re: Tips: IKEA SPARSNÄS Energidisplay

Inlägg av adent »

Enlig inlägg i facebookgrupp har Ikea i Jönköping dem för 99 kr nu!
ToPNoTCH
Inlägg: 4847
Blev medlem: 21 december 2009, 17:59:48

Re: Tips: IKEA SPARSNÄS Energidisplay

Inlägg av ToPNoTCH »

@Sommarlov

Satan vad bra !!!

Jag stod inför att göra samma grej och nu är det redan klart.
Jag väntar fortfarande på mina RFM69 (hoppas inte postnord tar dom, eldar upp dom eller bara skiter i att leverera) men kommer prova direkt.
ToPNoTCH
Inlägg: 4847
Blev medlem: 21 december 2009, 17:59:48

Re: Tips: IKEA SPARSNÄS Energidisplay

Inlägg av ToPNoTCH »

Fastnade lite i kompileringen med "codeLibrary" kommandona.

Vad kommer dom ifrån ?
Användarvisningsbild
sommarlov
Inlägg: 514
Blev medlem: 28 november 2015, 19:03:40
Ort: 08

Re: Tips: IKEA SPARSNÄS Energidisplay

Inlägg av sommarlov »

Det är mina egna utilities, i princip bara wifi och små utilities. tex codeLibrary.wait(10); är bara en delay(10); , fast med lite tweaks av mig. Som jag skrev i min första post, ta koden ovan som en grund och skruva till det så det passar dig.

Så här blev mitt "kort", samt min breadboard version.
Du har inte behörighet att öppna de filer som bifogats till detta inlägg.
Kiwawa
Inlägg: 1
Blev medlem: 30 december 2017, 13:39:36

Re: Tips: IKEA SPARSNÄS Energidisplay

Inlägg av Kiwawa »

Jag har moddat koden till sparsnas_decoder på GITHub så att den även skriver i en MySQL-databas. Detta skall jag sedan försöka göra något snyggt med i Grafana. Har försökt leta efter ett sätt att kontakta @strigeus för att se om man kan addera koden, men det gick inte på GITHub så jag postar det här med.
sm7tix
Inlägg: 283
Blev medlem: 19 september 2013, 10:47:03
Ort: Kristianstad

Re: Tips: IKEA SPARSNÄS Energidisplay

Inlägg av sm7tix »

Jag har en Sparsnäs liggande som inte funkar på min central. Finns i Kristianstad om någon vill köpa den.
Stefan

Mvh Stefan
Användarvisningsbild
carpelux
Inlägg: 1865
Blev medlem: 13 oktober 2007, 12:33:33
Ort: Varnhem

Re: Tips: IKEA SPARSNÄS Energidisplay

Inlägg av carpelux »

Kan du tänka dig att skicka så köper jag den gärna.
sm7tix
Inlägg: 283
Blev medlem: 19 september 2013, 10:47:03
Ort: Kristianstad

Re: Tips: IKEA SPARSNÄS Energidisplay

Inlägg av sm7tix »

carpelux skrev:Kan du tänka dig att skicka så köper jag den gärna.
Helst vill jag att den hämtas. Vi får se om någon vill det.

Mvh Stefan
Användarvisningsbild
carpelux
Inlägg: 1865
Blev medlem: 13 oktober 2007, 12:33:33
Ort: Varnhem

Re: Tips: IKEA SPARSNÄS Energidisplay

Inlägg av carpelux »

Jag förstår. Intresset kvarstår dock om det skulle komma dithän att du inte blir av med den lokalt.
sm7tix
Inlägg: 283
Blev medlem: 19 september 2013, 10:47:03
Ort: Kristianstad

Re: Tips: IKEA SPARSNÄS Energidisplay

Inlägg av sm7tix »

carpelux skrev:Jag förstår. Intresset kvarstår dock om det skulle komma dithän att du inte blir av med den lokalt.
Jag återkommer. Gott nytt år!

Mvh Stefan
Skriv svar