Das geht ziemlich sicher auf einem Uno, ein Nano kann das auch 
Ich habe das mal für einen anderen SML-Zähler geschrieben - vielleicht kannst Du es verwenden. Der "Zähler-Nano" ist über I²C mit einem zweiten verbunden, der als Modbus RTU-Server läuft.
#include <Arduino.h>
#include <AltSoftSerial.h>
#include <Wire.h>
#include <FastCRC.h>
// Built-in LED
#define LED 13
// Bit switch to activate debug output on pin 6
#define DEBUG_ON 6
// HOTWIRE connected to HOTWIRE pin 2 on Nano MODBUS
#define HOTWIRE 2
// States for the loop() state machine
enum STATES : uint8_t { INIT=0, FIND_PAUSE, WAIT_DATA, IN_PACKET, TRAILER, READ_DATA, WRITE_REFINED, FINISH_WRITE };
STATES state = INIT;
// Serial interface to optical readout
AltSoftSerial D0IN;
// Library to calculate CRC values
FastCRC16 CRC16;
// Input buffer for packets arriving on D0IN
const uint16_t BUFFERSIZE(400);
uint8_t data[BUFFERSIZE];
uint16_t PACKETLENGTH; // EMH meter: 388 bytes to be exoected
// Two blocks of data memory to hold values for Nano MODBUS.
// activeBlock toggles between both, one is "active" for Nano MODBUS I²C requests,
// the other is "inactive" to write changes received from D0IN.
const uint16_t MEMSIZE(256);
uint8_t memoryBlock[2][MEMSIZE];
uint8_t activeBlock = 0;
#define activeMem memoryBlock[activeBlock&1]
#define inactiveMem memoryBlock[(activeBlock^1)&1]
#define switchMem { activeBlock &= 1; activeBlock ^= 1; }
// Variables to hold requested address and length (in bytes) on I²C
uint16_t memAddr = 0;
uint16_t memLen = 0;
// Class to hold OBIS byte sequences to process
enum O_TYPE : uint8_t { O_BYTE=0, RES1, RES2, RES3, O_BOOL, O_INT, O_UINT, O_LIST, O_FLOAT=0x7F };
const int SEQLEN(12);
class Sequence
{
public:
bool complete; // While parsing, set to true if the value was found
bool inactive; // Flag to premanently disable this sequence
bool S_once; // If found once, this sequence will be switched to inactive
uint8_t S_ptr; // While parsing, index in seq successfully recognized
uint8_t S_len; // Length of sequence
uint16_t S_addr; // Target address when writing I²C
uint16_t S_tlen; // maximum length of target data
O_TYPE S_typ; // Target data type
uint8_t S_seq[SEQLEN]; // Sequence of bytes to be recognized
Sequence(uint8_t l, uint16_t a, bool o1, O_TYPE t, uint16_t tl, uint8_t s[])
{
reset();
inactive = false;
S_len = (l>SEQLEN) ? SEQLEN : l;
S_addr = a;
S_once = o1;
S_typ = t;
S_tlen = tl;
memset(S_seq, 0, SEQLEN);
memcpy(S_seq, s, S_len);
}
~Sequence() {}
inline void reset() { S_ptr = 0; complete = false; }
inline uint8_t check(uint8_t b)
{
if(b==S_seq[S_ptr])
{
if(S_ptr<S_len) S_ptr++;
}
else
{
S_ptr = 0;
}
return S_len-S_ptr;
}
};
const uint8_t SEQUENCES(9);
Sequence sequences[SEQUENCES] =
{
// len, addr, once, type, tl, sequence
Sequence( 8, 2, false, O_FLOAT, 4, (uint8_t []){0x77, 0x07, 0x01, 0x00, 0x10, 0x07, 0x00, 0xFF } ),
Sequence( 8, 6, false, O_FLOAT, 4, (uint8_t []){0x77, 0x07, 0x01, 0x00, 0x01, 0x08, 0x00, 0xFF } ),
Sequence( 8, 10, false, O_FLOAT, 4, (uint8_t []){0x77, 0x07, 0x01, 0x00, 0x02, 0x08, 0x00, 0xFF } ),
Sequence( 8, 14, false, O_FLOAT, 4, (uint8_t []){0x77, 0x07, 0x01, 0x00, 0x01, 0x08, 0x01, 0xFF } ),
Sequence( 8, 18, false, O_FLOAT, 4, (uint8_t []){0x77, 0x07, 0x01, 0x00, 0x02, 0x08, 0x01, 0xFF } ),
Sequence( 8, 22, false, O_FLOAT, 4, (uint8_t []){0x77, 0x07, 0x01, 0x00, 0x01, 0x08, 0x02, 0xFF } ),
Sequence( 8, 26, false, O_FLOAT, 4, (uint8_t []){0x77, 0x07, 0x01, 0x00, 0x02, 0x08, 0x02, 0xFF } ),
Sequence( 8, 30, true, O_BYTE, 4, (uint8_t []){0x77, 0x07, 0x81, 0x81, 0xC7, 0x82, 0x03, 0xFF } ),
Sequence( 8, 34, true, O_BYTE, 12, (uint8_t []){0x77, 0x07, 0x01, 0x00, 0x00, 0x00, 0x09, 0xFF } ),
};
// Header bytes to detect a packet on D0IN
const unsigned char header[] = { 0x1B, 0x1B, 0x1B, 0x1B, 0x01, 0x01, 0x01, 0x01, 0x00 };
// Trailer bytes to find the end of a D0IN packet
const unsigned char trailer[] = { 0x1B, 0x1B, 0x1B, 0x1B, 0x1A, 0x00 };
#ifdef TESTING
const uint16_t BUFLEN(64);
char buffer[BUFLEN];
// Helper function to generate a printable hexadecimal dump
// head: printed first
// sep: separator char printed between each byte. Omitted, if ==0
// data: pointer to data to be dumped
// length: number of bytes in data
// buffer: target to be printed into
// max_buffer_length: obvious... ;)
uint16_t hexDump(const char *head, const char sep, uint8_t *data, uint16_t length, char *buffer, uint16_t max_buffer_length)
{
uint16_t maxByte = 0; // maximum byte to fit in buffer
uint16_t outLen = 0; // used length in buffer
uint8_t byteLen = (sep?3:2); // length of a single byte dumped
uint16_t headLen = strlen(head); // length of header
const char *PRINTABLES = "0123456789ABCDEF";
// if not even the header will fit into buffer, bail out.
if(headLen>max_buffer_length) return 0;
// if we have a header, print it.
if(headLen) strcpy(buffer, head);
outLen = headLen;
// Calculate number of bytes fitting into remainder of buffer
maxByte = (max_buffer_length - headLen) / byteLen;
// If more than neede, reduce accordingly
if(length<maxByte) maxByte = length;
// May we print at least a single byte?
if(maxByte>0)
{
// Yes. Start behind the header
char *cp = buffer + headLen;
// For each byte...
for(uint16_t i=0; i<maxByte; ++i)
{
// ...print it, plus...
*cp++ = PRINTABLES[(data[i]>>4)&0x0F];
*cp++ = PRINTABLES[data[i]&0x0F];
outLen += 2;
// .. a separator, if it is defined and another byte is to follow
if(sep && i<maxByte-1)
{
*cp++ = sep;
outLen++;
}
}
*cp = 0;
}
return outLen;
}
#endif
// Helper function to get type and (extended) length information
// bytePtr: pointer to TL byte in byte stream
// type: variable reference to return type information
// length: variable reference to return length information
// advanceBytes: number of bytes to skip to get to the value proper
// Function returns 0 if okay or !=0 to denote an error or unlikely data
// 0: OK
// 1: reserved data type - unlikely
// 2: impossible length
// 3: length bigger than expected
uint16_t getLengthType(uint8_t *bytePtr, O_TYPE& type, uint16_t& length, uint16_t& advanceBytes, uint16_t expectedLength)
{
uint8_t *ptr = bytePtr;
// At least one TL byte
advanceBytes = 1;
// Get data type
type = (O_TYPE)(((*ptr)>>4) & 0x07);
// Reserved? Unlikely...
if(type==RES1 || type==RES2 || type==RES3) return 1;
// Get (first) length nibble
length = (*ptr)&0x0F;
while(*ptr&0x80) // extended length byte
{
ptr++;
length <<= 4;
length |= (*ptr)&0x0F;
advanceBytes++;
}
// length pointing behind complete packet? Impossible!
if((bytePtr-data)+length>PACKETLENGTH-8) return 2;
length -= advanceBytes;
// Length bigger than anticipated?
if(length>expectedLength) return 3;
// Looks okay.
return 0;
}
// onRequest handler function for I²C
// Writes requested memLen bytes out of the "active" memory block, starting from address memAddr
void I2C_send()
{
Wire.write(activeMem+memAddr, memLen);
}
// onReceive handler function for I²C
// Expects 4-byte packets: addrHI, addrLO, lenHI, lenLO
void I2C_addr(int cnt)
{
if(cnt==4)
{
memAddr = Wire.read();
memAddr <<= 8;
memAddr |= Wire.read();
memAddr -= 1; // MODBUS address starts at 1
memAddr *= 2; // MODBUS units are 16 bit, not bytes. So *2 is required
if(memAddr>MEMSIZE) memAddr = 0; // Overflow?
memLen = Wire.read();
memLen <<= 8;
memLen |= Wire.read();
memLen *= 2; // MODBUS units are 16 bit, not bytes. So *2 is required
if(memAddr+memLen>MEMSIZE) memAddr = 0; // Overflow?
}
else
{
// unexpected packet length. Fall back to safe values
memAddr = 0;
memLen = 2;
while(Wire.available()) Wire.read();
}
}
void setup() {
// Set up LED
pinMode(LED, OUTPUT);
digitalWrite(LED, LOW);
// Set up data write enable pin
pinMode(HOTWIRE, INPUT_PULLUP);
// Set up raw data/refined data jumper pin
pinMode(DEBUG_ON, INPUT_PULLUP);
// Start Serial interface for photo sensor
D0IN.begin(9600);
// Start I²C as Slave #2
Wire.begin(2);
Wire.setClock(400000L);
Wire.onReceive(I2C_addr);
Wire.onRequest(I2C_send);
// Init read buffer
memset(data, 0, BUFFERSIZE);
state = INIT;
// Init data memory - both blocks
memset(memoryBlock, 0, 2*MEMSIZE);
#ifdef TESTING
// Init Serial - in case we are doing test outputs
Serial.begin(115200);
Serial.println("");
#endif
}
void loop()
{
static uint8_t *cp = data;
static uint8_t Startseq = 0;
static uint8_t Endseq = 0;
static uint8_t Trailerbytes = 0;
static bool byteWaiting = false;
static uint8_t byteRead = 0;
const uint32_t PAUSETIME(200);
static uint32_t pause = millis();
static uint16_t cnt = 0;
static uint8_t rc = 0;
static uint8_t field = 0;
static uint32_t packetOK = 0;
static uint32_t packetERR = 0;
#ifdef TESTING
static bool TRACE_ON = false;
// Is trace output required?
if(digitalRead(DEBUG_ON)==LOW)
{
TRACE_ON = true;
}
else
{
TRACE_ON = false;
}
#endif
// May we read another byte?
if(!byteWaiting && D0IN.available())
{
// Yes. Do it.
byteRead = D0IN.read();
byteWaiting = true;
}
switch(state)
{
case INIT:
state = FIND_PAUSE;
break;
case FIND_PAUSE:
// Detect pause
if(!byteWaiting)
{
// Did we pass the pause time?
if(millis()-pause>=PAUSETIME)
{
// Yes. Go on catch a packet.
Startseq = 0;
cp = data;
cnt = 0;
state = WAIT_DATA;
}
}
else
{
byteWaiting = false;
pause = millis();
}
break;
case WAIT_DATA:
// Pause found. Now find data packet
if(byteWaiting)
{
// Is it the next in the header sequence?
if(byteRead==header[Startseq])
{
// Yes. collect it and wait for the next
*cp++ = byteRead;
cnt++;
// Overflow?
if(cnt>=BUFFERSIZE)
{
// Yes. Reset data and start over
state = FIND_PAUSE;
}
else
{
Startseq++;
// ...unless it was the 4th. Then we have found the start sequence and may go on
if(header[Startseq]==0x00)
{
Endseq = 0;
state = IN_PACKET;
digitalWrite(LED, HIGH);
}
}
}
else
{
// No - garbage or defective packet. Reset to begin the search again
cnt = 0;
cp = data;
Startseq = 0;
}
byteWaiting = false;
}
break;
case IN_PACKET:
// Start sequence was there - read on
if(byteWaiting)
{
*cp++ = byteRead;
cnt++;
// Overflow?
if(cnt>=BUFFERSIZE)
{
// Yes. Reset data and start over
digitalWrite(LED, LOW);
state = FIND_PAUSE;
}
else
{
// Is it part of the ending sequence?
if(byteRead==trailer[Endseq])
{
// Yes. count it.
Endseq++;
// If we had 4 in a row, proceed to catch the trailer
if(trailer[Endseq]==0x00)
{
Trailerbytes = 0;
state = TRAILER;
}
}
else
{
// No - something else - discard count
Endseq = 0;
}
}
byteWaiting = false;
}
break;
case TRAILER:
// Get the final 3 bytes
if(byteWaiting)
{
// Catch it.
*cp++ = byteRead;
cnt++;
// Overflow?
if(cnt>=BUFFERSIZE)
{
// Yes. Reset data and start over
digitalWrite(LED, LOW);
state = FIND_PAUSE;
}
else
{
Trailerbytes++;
if(Trailerbytes==3)
{
state = READ_DATA;
}
}
byteWaiting = false;
}
break;
case READ_DATA:
// Did we get some?
if(cnt)
{
PACKETLENGTH = cnt;
// Calculate CRC
uint16_t crc = CRC16.x25(data, PACKETLENGTH-2);
// Get CRC from packet
uint16_t packetCRC = (data[PACKETLENGTH-1]<<8)|data[PACKETLENGTH-2];
#ifdef TESTING
if(TRACE_ON)
{
uint16_t pos = 0;
while(pos<cnt)
{
hexDump(" ", ' ', data+pos, (cnt-pos)<16 ? (cnt-pos) : 16, buffer, BUFLEN);
Serial.println(buffer);
pos += 16;
}
Serial.flush();
}
#endif
// Matching CRC? If so, proceed to processing. Else discard packet.
if(packetCRC==crc)
{
packetOK++;
state = WRITE_REFINED;
}
else
{
#ifdef TESTING
if(TRACE_ON)
{
Serial.println("CRC err");
Serial.flush();
}
#endif
packetERR++;
digitalWrite(LED, LOW);
state = FIND_PAUSE;
}
}
else
{
// No. Wait for another packet
digitalWrite(LED, LOW);
state = FIND_PAUSE;
}
break;
case WRITE_REFINED:
{
// Yes.
cp = data;
// Data proper
uint16_t lcnt = 0;
for(uint8_t i=0; i<SEQUENCES; i++)
{
sequences[i].reset();
}
while(rc==0 && lcnt<cnt)
{
// Check all sequences for a match
for(uint8_t i=0; i<SEQUENCES; ++i)
{
if(sequences[i].complete || sequences[i].inactive) continue;
// Sequence completely matching?
if(sequences[i].check(*(cp+lcnt))==0)
{
// Yes, found it.
// We will have a sequence of data fields to process:
// - status
// - valTime
// - unit code
// - scaler
// - value
uint8_t *cq = cp + lcnt + 1;
uint16_t skipLen = 0;
int scaler = 0;
O_TYPE type;
uint16_t length;
uint16_t stepV;
rc = 0;
field = 0;
#ifdef TESTING
if(TRACE_ON)
{
hexDump("S:", ' ', sequences[i].S_seq, sequences[i].S_len, buffer, BUFLEN);
Serial.println(buffer);
Serial.flush();
}
#endif
// status: skip
if((rc = getLengthType(cq, type, length, stepV, 4)))
{
field = 1;
break;
}
cq += stepV + length;
skipLen += stepV + length;
// valTime: skip
if((rc = getLengthType(cq, type, length, stepV, 8)))
{
field = 2;
break;
}
cq += stepV + length;
skipLen += stepV + length;
// unit code: skip
if((rc = getLengthType(cq, type, length, stepV, 2)))
{
field = 3;
break;
}
cq += stepV + length;
skipLen += stepV + length;
// scaler: we will need it for O_FLOAT target values
if((rc = getLengthType(cq, type, length, stepV, 2)))
{
field = 4;
break;
}
if(length==1) scaler = (signed char)*(cq+stepV);
cq += stepV + length;
skipLen += stepV + length;
// value: depending on type, different treatments necessary
if((rc = getLengthType(cq, type, length, stepV, 100)))
{
field = 5;
break;
}
#ifdef TESTING
if(TRACE_ON)
{
hexDump("V:", ' ', cq, stepV+length, buffer, BUFLEN);
Serial.println(buffer);
Serial.flush();
}
#endif
cq += stepV;
// Just in case - we must have at least one byte
if(length>0)
{
bool sign = false;
switch(type)
{
case O_BYTE:
if(length>sequences[i].S_tlen)
{
// Double writes to both memory blocks, since we may be interrupted by I²C any time
// Write data to inactive memory first...
memcpy(inactiveMem+sequences[i].S_addr, cq, sequences[i].S_tlen);
// Switch memory blocks to have a valid value in the active block...
switchMem
// Write again to have both blocks synchronized
memcpy(inactiveMem+sequences[i].S_addr, cq, sequences[i].S_tlen);
}
else
{
memcpy(inactiveMem+sequences[i].S_addr, cq, length);
switchMem
memcpy(inactiveMem+sequences[i].S_addr, cq, length);
}
break;
case O_BOOL:
inactiveMem[sequences[i].S_addr] = *cq;
switchMem
inactiveMem[sequences[i].S_addr] = *cq;
break;
case O_INT:
sign = true;
case O_UINT:
// Conversion into float required?
if(sequences[i].S_typ==O_FLOAT)
{
// Yes. Beware: Arduino floats and longs are 32 bits maximum!
// OBIS values can be longer, so we need to truncate or scale down.
// First we try to get the sign of the number
float Minus = 1.0;
// Can we expect negative values (type O_INT)?
if(sign)
{
// Yes. is the very first bit set?
if((*cq&0x80))
{
// Yes, we have a negative number here
Minus = -1.0;
}
else
{
// No, positive - we may treat it like an unsigned.
sign = false;
}
}
float f = 0.0;
for(uint8_t j=0; j<length; ++j)
{
f *=256.0;
if(sign)
{
f += (~(*(cq+j)))&0xFF;
}
else
{
f += *(cq+j);
}
}
if(sign) f += 1.0;
f *= Minus;
// apply the scaler
if(scaler>0)
{
for(int j=scaler;j;--j) f *= 10.0;
}
else if(scaler<0)
{
for(int j=scaler;j;++j) f /= 10.0;
}
// Write 4 bytes of float in MSB order
uint32_t uf = *(uint32_t *)&f; // Ugly... Not portable, but unavoidable
inactiveMem[sequences[i].S_addr] = (uf>>24) & 0xFF;
inactiveMem[sequences[i].S_addr+1] = (uf>>16) & 0xFF;
inactiveMem[sequences[i].S_addr+2] = (uf>>8) & 0xFF;
inactiveMem[sequences[i].S_addr+3] = uf & 0xFF;
switchMem
memcpy(inactiveMem+sequences[i].S_addr, activeMem+sequences[i].S_addr, 4);
#ifdef TESTING
if(TRACE_ON)
{
Serial.println(f);
Serial.flush();
}
#endif
}
else
{
if(length>sequences[i].S_tlen)
{
memcpy(inactiveMem+sequences[i].S_addr, cq, sequences[i].S_tlen);
switchMem
memcpy(inactiveMem+sequences[i].S_addr, cq, sequences[i].S_tlen);
}
else
{
memcpy(inactiveMem+sequences[i].S_addr, cq, length);
switchMem
memcpy(inactiveMem+sequences[i].S_addr, cq, length);
}
}
break;
default: // All else does not interest us
break;
}
}
cq += length;
skipLen += stepV + length;
// signature: skip
if((rc = getLengthType(cq, type, length, stepV, 32)))
{
field = 6;
break;
}
cq += stepV + length;
skipLen += stepV + length;
// disable found sequence for this turn
sequences[i].complete = true;
if(sequences[i].S_once) sequences[i].inactive = true;
lcnt += skipLen - 1;
break;
}
}
lcnt++;
}
state = FINISH_WRITE;
}
break;
case FINISH_WRITE:
{
// Write read length, to address 0x0000
inactiveMem[0] = (PACKETLENGTH>>8)&0xFF;
inactiveMem[1] = PACKETLENGTH&0xff;
// Write return code, error field indicator and packet counts, to address 0x002E
// Field indicator - >0, if field in error
inactiveMem[46] = field&0xFF;
// Return code from field length/type detection
inactiveMem[47] = rc&0xff;
// Number of intact packets processed
inactiveMem[48] = (packetOK>>24)&0xFF;
inactiveMem[49] = (packetOK>>16)&0xFF;
inactiveMem[50] = (packetOK>>8)&0xFF;
inactiveMem[51] = packetOK&0xFF;
// Number of defective packets discarded
inactiveMem[52] = (packetERR>>24)&0xFF;
inactiveMem[53] = (packetERR>>16)&0xFF;
inactiveMem[54] = (packetERR>>8)&0xFF;
inactiveMem[55] = packetERR&0xFF;
// Now switch blocks again and do a copy in the now inactive block
switchMem
inactiveMem[0] = (PACKETLENGTH>>8)&0xFF;
inactiveMem[1] = PACKETLENGTH&0xff;
memcpy(inactiveMem+46, activeMem+46, 10);
digitalWrite(LED, LOW);
// Go fetch next
state = FIND_PAUSE;
}
break;
};
}