Re: Hemautomation V2
Postat: 13 mars 2016, 07:47:18
Snyggt! Det finns väl en del lib skrivna till manchester code. Borde fungera på den signalen.
Svenskt forum för elektroniksnack.
https://elektronikforumet.com/forum/
Kod: Markera allt
High signal delay:
278 µS,
Low signal delay:
300 µS,
1343 µS,
Partial data:
---------------100110101010101010010110011010100101101001101010010
010100101101010100110101010101010010110011010100101101001101010010
010100101101010100110101010101010010110011010100101101001101010010
010100101101------------------------------------------------------
Complete data:
010100101101010100110101010101010010110011010100101101001101010010
Parsed data:
0 00110000100000001101000110 0 1 00 01 0
ID: 12714822
Group: 0
Value: 1
Channel: 0
Button: 2
Det fungerar nog inte, det är nämligen så att säga två stegs kodning. Först är det manchester kodning (att en 0'a är 01 och en 1'a 10) men sen när man väl sänder denna manchesterkodade datan så är en etta en hög puls följt av ett kort uppehåll medan en nolla är en hög puls följt av ett långt uppehåll. Sen är startbiten en hög följd av ett uppehåll av en tredje längd. För att sedan göra saker ännu knöligare så följer dimningen inte manchesterkodning utan där blir det två ettor istället vilket egentligen inte skall kunna följa varandra. Men detta är gissningsvis en efterimplementation för att kunna styra dimmers med absolut värde utan att ändra protokollet för mycket kan jag tänka mig.Det finns väl en del lib skrivna till manchester code. Borde fungera på den signalen.
Kod: Markera allt
/* 433 MHz transmitter for wireless wall sockets. Tested with NEXA, ANSLUT and PROOVE brands.
* Works for on/off switches and dimmers.
*
* Daniel Falk
* DA-Robotteknik
* +46 (0) 76-80 156 12
* daniel @T da-robotteknik.se
*
* 2016
* You are free to use and share but please leave a acknowledge.
*
*/
#ifndef NEXA_H
#define NEXA_H
#include "Arduino.h"
// Define delays for different part of the signal
#define HIGH_DELAY 278 //µS
#define LOW_DELAY_ONE 300 //µS
#define LOW_DELAY_ZERO 1343 //µS
#define LOW_DELAY_START 3174 // Delay after startbit (µS)
#define LOW_DELAY_NEW_TRANSMIT 12941 // Time between repeated transmissions (µS)
// Define length of data
#define LENGTH_ID 26
#define LENGTH_GROUP 1
#define LENGTH_VALUE 1
#define LENGTH_CHANNEL 2
#define LENGTH_BUTTON 2
#define LENGTH_DIM 4
class Nexa {
public:
Nexa(byte txPin);
// Send a command to put on a receiver
void sendOn(long id, byte button);
// Send a command to put off a receiver
void sendOff(long id, byte button);
// Send a command to put dimmer receiver to an absolute value (1-16)
void sendDim(long id, byte button, byte dimValue);
private:
const byte txPin_;
boolean idArray[LENGTH_ID];
boolean groupArray[LENGTH_GROUP];
boolean valueArray[LENGTH_VALUE];
boolean channelArray[LENGTH_CHANNEL];
boolean buttonArray[LENGTH_BUTTON];
boolean dimArray[LENGTH_DIM];
// Send a command to a receiver
void sendCommand(long id, byte button, boolean value, byte dim);
void setBits(boolean * dst, long number, byte length);
// Sends data given in predefined arrays (with start and stop bit appended) four times
void sendCommand(boolean dim);
// Send the given data
void sendPackage(boolean * data, byte length);
// Send a complete bit (ie one -> 0,1 and zero -> 1,0)
// value = 's' -> startbit / stopbit
// value = '1' -> one
// value = '0' -> zero
void sendBit(char value);
// Send raw bits
void sendRawBit(char value);
// Send a high pulse with specified on time
void highPulse(int delayHigh);
};
#endif
Kod: Markera allt
/* 433 MHz transmitter for wireless wall sockets. Tested with NEXA, ANSLUT and PROOVE brands.
* Works for on/off switches and dimmers.
*
* Daniel Falk
* DA-Robotteknik
* +46 (0) 76-80 156 12
* daniel @T da-robotteknik.se
*
* 2016
* You are free to use and share but please leave a acknowledge.
*
*/
#include "Nexa.h"
Nexa::Nexa(byte txPin) : txPin_(txPin) {
pinMode(txPin, OUTPUT);
}
// Send a command to put on a receiver
// ID = transmitter ID (0 ... 67M)
// button = 1, 2, 3 or 4
void Nexa::sendOn(long id, byte button){
sendCommand(id, button, 1, 0);
}
// Send a command to put off a receiver
// ID = transmitter ID (0 ... 67M)
// button = 1, 2, 3 or 4
void Nexa::sendOff(long id, byte button){
sendCommand(id, button, 0, 0);
}
// Send a command to put dimmer receiver to an absolute value
// ID = transmitter ID (0 ... 67M)
// button = 1, 2, 3 or 4
// dimValue = 0...15
void Nexa::sendDim(long id, byte button, byte dimValue){
sendCommand(id, button, 0, dimValue);
}
// Send a command to a receiver
void Nexa::sendCommand(long id, byte button, boolean value, byte dim){
setBits(idArray, id, LENGTH_ID);
setBits(groupArray, 0, LENGTH_GROUP);
setBits(valueArray, value, LENGTH_VALUE);
setBits(channelArray, 0, LENGTH_CHANNEL);
setBits(buttonArray, button - 1, LENGTH_BUTTON);
setBits(dimArray, dim, LENGTH_DIM);
sendCommand(dim!=0);
}
void Nexa::setBits(boolean * dst, long number, byte length){
for(byte i = 1; i <= length; i++)
dst[length-i] = number & ((long)1 << (i-1));
}
// Sends data given in predefined arrays (with start and stop bit appended) four times
void Nexa::sendCommand(boolean dim){
for (int i = 0; i < 4; i ++){
sendBit('s');
sendPackage(idArray, LENGTH_ID);
sendPackage(groupArray, LENGTH_GROUP);
if (!dim)
sendPackage(valueArray, LENGTH_VALUE);
else{
sendRawBit('1'); // Dimmer call is only case in NEXA protocol where two raw bits can have the same value..
sendRawBit('1');
}
sendPackage(channelArray, LENGTH_CHANNEL);
sendPackage(buttonArray, LENGTH_BUTTON);
if (dim)
sendPackage(dimArray, LENGTH_DIM);
sendBit('s');
delayMicroseconds(LOW_DELAY_NEW_TRANSMIT-LOW_DELAY_START);
}
}
// Send the given data
void Nexa::sendPackage(boolean * data, byte length){
for (int i = 0; i < length; i ++)
sendBit('0' + data[i]);
}
// Send a complete bit (ie one -> 0,1 and zero -> 1,0)
// value = 's' -> startbit / stopbit
// value = '1' -> one
// value = '0' -> zero
void Nexa::sendBit(char value){
if (value == 's')
sendRawBit(value);
else if (value == '0'){
sendRawBit('1');
sendRawBit('0');
}
else if (value == '1'){
sendRawBit('0');
sendRawBit('1');
}
}
// Send raw bits
void Nexa::sendRawBit(char value){
if (value != 's' && value != '0' && value != '1')
return;
highPulse(HIGH_DELAY);
if (value == 's')
delayMicroseconds(LOW_DELAY_START);
else if (value == '1')
delayMicroseconds(LOW_DELAY_ONE);
else if (value == '0')
delayMicroseconds(LOW_DELAY_ZERO);
}
// Send a high pulse with specified on time
void Nexa::highPulse(int delayHigh){
digitalWrite(txPin_, HIGH);
delayMicroseconds(delayHigh);
digitalWrite(txPin_, LOW);
}
Kod: Markera allt
/*
* Class for communicating over serial port. Data received is expected to be of format
* 's' data0 data1 ... dataN checksum
* where 's' is a start value and checksum is the xor of all data bytes 1..N.
*
* At correct receive and decode of data an acknowledge is sent back
* 'a' checksum
* where checksum is the same as what was received.
*
* Daniel Falk
* DA-Robotteknik
* +46 (0) 76-80 156 12
* daniel @T da-robotteknik.se
*
* 2016
* You are free to use and share but please leave a acknowledge.
*/
#ifndef SERIAL_COM_H
#define SERIAL_COM_H
class SerialComm {
public:
// Set up a serial object that can receive commands with dataLength bytes.
SerialComm(int dataLength, long baudRate);
// Check if there is new data in buffer and try to decode it
// Returns true if data package was succesfully decoded
boolean processBuffer();
// Get the i:th byte received
byte getByte(byte i);
// Get an int stored in i:th (high byte) and j:th (low byte) byte
int getInt(byte i, byte j);
// Get a long stored in i:th (highest byte) to the j:th (lowest byte) byte
long getLong(byte i, byte j);
private:
byte *serialDecodeBuffer;
byte serialDecoderBufferSize;
byte serialDecodeBufferStart; // Index of first byte in decode buffer
byte serialDecodeBufferStop; // Next free index in decode buffer
boolean checksumOk;
// Drop bytes until the first byte in decode buffer is a startbyte
void shiftToStart();
// Pull out and remove the first byte in decode buffer
byte popByte();
// Peek on i:th byte in decode buffer (without removing it)
byte peekByte(byte i);
// Add a byte to the end of decode buffer
void addByte(byte b);
// Is decode buffer full?
boolean decodeBufferFull();
// Is decode buffer empty?
boolean decodeBufferEmpty();
// Check how many byte that is in the decode buffer
int inBuffer();
};
#endif
Kod: Markera allt
/*
* Class for communicating over serial port. Data received is expected to be of format
* 's' data0 data1 ... dataN checksum
* where 's' is a start value and checksum is the xor of all data bytes 1..N.
*
* At correct receive and decode of data an acknowledge is sent back
* 'a' checksum
* where checksum is the same as what was received.
*
* Daniel Falk
* DA-Robotteknik
* +46 (0) 76-80 156 12
* daniel @T da-robotteknik.se
*
* 2016
* You are free to use and share but please leave an acknowledge.
*/
#include "SerialComm.h"
// Set up a serial object that can receive commands with dataLength bytes.
SerialComm::SerialComm(int dataLength, long baudRate){
// Memoryspace for startbyte, data bytes and checksum
serialDecoderBufferSize = dataLength + 2 + 1; // Buffer is one more than bytes used in transmission
serialDecodeBuffer = new byte[serialDecoderBufferSize];
serialDecodeBufferStart = 0; // Index of first byte in buffer
serialDecodeBufferStop = 0; // Index of next free space in buffer
checksumOk = 0;
Serial.begin(baudRate);
}
// Check if there is new data in buffer and try to decode it
// Returns true if data package was succesfully decoded
boolean SerialComm::processBuffer(){
if (checksumOk)
popByte(); // Move to next package
checksumOk = 0;
shiftToStart();
// If bytes in input buffer fill up decode buffer
while (Serial.available() && !decodeBufferFull()){
// Get byte from UART buffer and put in decoder buffer
addByte(Serial.read());
// Drop bytes if the startbyte isn't the first
shiftToStart();
}
// If decode buffer is full (i.e. whole command has been received) then check the checksum and answer
if (decodeBufferFull()){
byte checksum = peekByte(1);
for (int i = 2; i < serialDecoderBufferSize - 2; i ++)
checksum = checksum ^ peekByte(i);
if (checksum == peekByte(serialDecoderBufferSize-2)){
Serial.write('a');
Serial.write(checksum);
checksumOk = 1;
} else {
// Look for the next start byte
popByte(); // Remove current start byte
processBuffer(); // Redo process until either no more data in USART buffer or data successfully decoded
}
}
return checksumOk;
}
// Get the i:th byte received
byte SerialComm::getByte(byte i){
if (!checksumOk)
return 0;
return peekByte(i+1); // skip the start byte
}
// Get an int stored in i:th (high byte) and j:th (low byte) byte
int SerialComm::getInt(byte i, byte j){
if (!checksumOk)
return 0;
return ((int)peekByte(i+1))<<8 + peekByte(j+1); // skip the start byte
}
// Get a long stored in i:th (highest byte) to the j:th (lowest byte) byte
long SerialComm::getLong(byte i, byte j){
if (!checksumOk)
return 0;
long l = 0;
signed int bytes = j-i;
if (bytes < 0)
bytes = -bytes;
bytes++; // Number of bytes is one more than diff between start and stop numbers
byte index;
for (int k = 0; k < bytes; k ++){
if (i < j)
index = i+k;
else
index = i-k;
l = (l<<8) + peekByte(index+1); // Skip start byte in index calc
}
return l;
}
// Drop bytes until the first byte in decode buffer is a startbyte
void SerialComm::shiftToStart() {
while (serialDecodeBuffer[serialDecodeBufferStart] != 's' && !decodeBufferEmpty())
popByte();
}
// Pull out and remove the first byte in decode buffer
byte SerialComm::popByte(){
if (decodeBufferEmpty())
return 0;
byte b = serialDecodeBuffer[serialDecodeBufferStart];
serialDecodeBufferStart = (serialDecodeBufferStart<serialDecoderBufferSize-1)?serialDecodeBufferStart+1:0;
return b;
}
// Peek on i:th byte in decode buffer (without removing it)
byte SerialComm::peekByte(byte i){
if (inBuffer() < i)
return 0;
byte index = serialDecodeBufferStart + i;
if (index > serialDecoderBufferSize-1)
index = index - serialDecoderBufferSize;
return serialDecodeBuffer[index];
}
// Add a byte to the end of decode buffer
void SerialComm::addByte(byte b){
if (decodeBufferFull())
return;
serialDecodeBuffer[serialDecodeBufferStop] = b;
serialDecodeBufferStop = (serialDecodeBufferStop<serialDecoderBufferSize-1)?serialDecodeBufferStop+1:0;
}
// Is decode buffer full?
boolean SerialComm::decodeBufferFull() {
return inBuffer() >= serialDecoderBufferSize - 1;
}
// Is decode buffer empty?
boolean SerialComm::decodeBufferEmpty() {
return inBuffer() == 0;
}
// Check how many byte that is in the decode buffer
int SerialComm::inBuffer(){
if (serialDecodeBufferStop >= serialDecodeBufferStart)
return serialDecodeBufferStop-serialDecodeBufferStart;
else
return serialDecoderBufferSize - serialDecodeBufferStart + serialDecodeBufferStop;
}
Kod: Markera allt
#include "Nexa.h"
#include "SerialComm.h"
#define RADIO_TRANSMITTER_PIN 13
#define SERIAL_BAUD 115200
Nexa nexa(RADIO_TRANSMITTER_PIN);
SerialComm *serialComm;
void setup() {
// Start a new serial communication with Raspberry Pi.
// Use 7 data bytes (plus start and checksum)
// 4 bytes ID, 1 byte button nbr, 1 byte action command and 1 byte dim level
serialComm = new SerialComm(7, SERIAL_BAUD);
}
void loop() {
while(true){
if (serialComm->processBuffer()){
byte action = serialComm->getByte(5);
long id = serialComm->getLong(0,3);
byte button = serialComm->getByte(4);
byte dim = serialComm->getByte(6);
switch (action){
case 0:
nexa.sendOff(id, button);
break;
case 1:
nexa.sendOn(id, button);
break;
case 255:
nexa.sendDim(id, button, dim);
break;
}
}
}
}