Cheap dirty way to send a raw-mysensors message?
-
Hello,
is it possible to "inject" a sensor-value into the gateway by hand-crafting a data packet?
I recently got some of these little guys (https://wemakethings.net/chirp/)
Now my idea was to slap a NRF24 on the ISP header (+2 extra pins for CE and CSN).
So far so good .. now I want to craft a simple packet to get data to my gateways ..
This is what I got so far but my gateway is not picking up anything.
radio.begin(); // Start up the radio radio.setDataRate(RF24_250KBPS); radio.setAutoAck(1); // Ensure autoACK is enabled radio.setRetries(15,15); // Max delay between retries & number of retries byte station_address[] = { 0x00, 0xFC, 0xE1, 0xA8, 0xA8 }; //byte station_address[] = { 0xA8, 0xA8, 0xE1, 0xFC, 0x00 }; radio.openWritingPipe(station_address); // Write to device address 0x00,0xFC,0xE1,0xA8,0xA8 radio.stopListening(); byte test[] = { 250, 250, 0, 0b10000001 /* version_length */, 0b00100001, 7 /* S_HUM */ , 1 /* V_HUM */, 123}; radio.write(test,8);
And no .. I can't cram the mysensors library on the ATTINY44.
I am currently happe that I got the basic chirp functunality for measuring the humidity of the soil, attiny debug serial and nrf24 all running on 4k of flash.
Der Sketch verwendet **4046 Bytes (98%)** des Programmspeicherplatzes. Das Maximum sind 4096 Bytes. Globale Variablen verwenden 150 Bytes (58%) des dynamischen Speichers, 106 Bytes für lokale Variablen verbleiben. Das Maximum sind 256 Bytes.
If anybody know if this is even possible I would like to hear from you.
Best regards Marc
-
The problem I think is that the gateway needs the node to register on the network so it knows what it should expect receiving, it is a 2 way communication not like those devices using RF link that only transmit and other only receives.
Have you evaluated the button size node on openhardware? I think it could be an alternative for your needs
-
@gohan Thanks for your response. The problem is that I already have 5 "chirps" and just later came up with the idea of adding nrf24 to them. I might try to add a "relay" node which will listen for "normal" rf24 packets and then send them to my gateway using the mysensors library.
-
After many hours trial and error I present you .. the raw-mysensors-client!!
MyGateway: ESP8266 + mysensorsMQTT
MyNode: https://github.com/Miceuz/PlantWateringAlarm
MyNode-MCU: Attiny44 (MemoryFootprint: 87% of 4KB)
ArduinoCore: https://github.com/SpenceKonde/ATTinyCore
ArduinoCore-PinLayout: Counterclockwise (like AttinyCore)
Note: The default "counterclockwise"pinout is the alternate pinout shown here: https://github.com/SpenceKonde/ATTinyCore/blob/master/avr/extras/ATtiny_x4.mdThe important hint is here (http://tmrh20.github.io/RF24/ATTiny.html)
Connect MOSI of the RF24 to PA5 of the attiny and MISO of the RF24 to PA6 of the attiny.
CE = 8
CSN = 7To get the connection done right DONT .. i mean DO NOT BECAUSE IT DOESNT FUCKING WORK connect MOSI of the RF24 to MOSI of the attiny. RF24 uses a embedded implementation of the USI-engine found on some AtTiny's.
radio-initilisation
radio.begin(); // Start up the radio radio.setDataRate(RF24_250KBPS); radio.setAutoAck(1); // Ensure autoACK is enabled radio.setRetries(15,15); // Max delay between retries & number of retries //radio.setPayloadSize(8); radio.enableDynamicPayloads(); radio.setPALevel(RF24_PA_MAX); byte tx_address[] = { 0x00, 0xFC, 0xE1, 0xA8, 0xA8 }; //byte tx_address[] = { 0xA8, 0xA8, 0xE1, 0xFC, 0x00 }; radio.openWritingPipe(tx_address); radio.stopListening();
Send-Function:
void sendHumidity(uint16_t humidity) { // snprintf_P(_fmtBuffer, MY_GATEWAY_MAX_SEND_LENGTH, PSTR("%s/%d/%d/%d/%d/%d"), prefix, message.sender, message.sensor, mGetCommand(message), mGetAck(message), message.type); // X8X22<\0><\n>!<7>7AY0;255;3;0;9;TSF:MSG:READ,50-50-0,s=55,c=1,t=7,pt=1,l=1,sg=0:65<\n> // 0;255;3;0;9;Sending message on topic: domoticz/in/MyMQTT/50/55/1/0/7<\n> #define LAST_NODE_ID 50 // last #define NODE_ID 50 // sender #define GATEWAY_ID 0 // destination // version_length // 5 bit - Length of payload // 1 bit - Signed flag // 2 bit - Protocol version // command_ack_payload // 3 bit - Payload data type // 1 bit - Is ack messsage - Indicator that this is the actual ack message. // 1 bit - Request an ack - Indicator that receiver should send an ack back. // 3 bit - Command type #define SENSOR_TYPE 7 // type S_HUM = 7 #define SENSOR_ID 55 // sensor-id byte test[] = { LAST_NODE_ID, NODE_ID, GATEWAY_ID, 0b00010010, 0b01100001, SENSOR_TYPE, SENSOR_ID, (uint8_t)(humidity & 0x00FF), (uint8_t)((humidity >> 8) & 0x00FF), 0x00 }; radio.write(test,9); }
I am not quite sure yet if the message-types etc. are right but I am trying to find this out. 'A' (dec 65) is the "value" of my humidity .. next step is to make this a real value ofc.
Proof at domoticz:
@gohan: In the file MyConfig.h I commented this out:
/** * @def MY_REGISTRATION_FEATURE * @brief If enabled, node has to register to gateway/controller before allowed to send sensor data. */ // #define MY_REGISTRATION_FEATURE
This will skip the whole hasse of registering the client properly (which I can't .. remember 87% of flash is full)
-
@cimba007 very nice work! Great way to be compatible with the MySensors protocol/ecosystem on a limited device when the full MySensors feature set is not needed.
-
I might have run into a bug in the calculation of message length.In my handcrafted packet I use this as the 4thy byte send:0b10000001So to craft this I looked at MyMessage.h which showed me this:uint8_t version_length; // 2 bit - Protocol version // 1 bit - Signed flag // 5 bit - Length of payload
So from my understanding:Protocol Version = 2 => 0b10
Signed Flag = 0 => 0b0
Length of Payload = 1 = 0b00001Which results in 0b10 0 00001 = 0b10000001But I get this error:LEN,8!=23
So .. where might this come from?radio.write(test,8);
Mycontroller received a packet with the length of 8 but expected a packet with the length of .. 23 ?!#define mGetLength(_message) ((uint8_t)BF_GET(_message.version_length, 3, 5)) //!< Get length field
Which in essential is BF_GET ..#define BF_GET(y, start, len) ( ((y)>>(start)) & BIT_MASK(len) ) //!< Extract a bitfield of length 'len' starting at bit 'start' from 'y'
So what is happening?BF_GET(0b10000001, 3, 5) ( ((0b10000001)>>(3)) & BIT_MASK(5) ) //!< Extract a bitfield of length 'len' starting at bit 'start'
Whoops? This will throw away all the length-information!!!
(0b10000001)>>(3)
This should result in:
0b11110000 & BIT_MASK5 = 0b1111000 & 0b00011111 = 0b00010000 = 16 decimalconst uint8_t expectedMessageLength = HEADER_SIZE + (mGetSigned(_msg) ? MAX_PAYLOAD : msgLength);
const uint8_t expectedMessageLength = 7+ 16); // = 23
Yeah .. the lib is right .. 8 != 23 .. but the handcrafted length = 1 + header_length (7) = 8Am I wrong or is this a bug?Edit: I might have read the comments in the source code the wrong way
-
From time to time I run into this bug .. not sure if this is related:
0;255;3;0;9;TSM:READY:NWD REQ<\n> 0;255;3;0;9;TSF:MSG:SEND,0-0-255-255,s=255,c=3,t=20,pt=0,l=0,sg=0,ft=0,st=OK:<\n> Fatal exception 28(LoadProhibitedCause):<\n> epc1=0x40202704, epc2=0x00000000, epc3=0x00000000, excvaddr=0x00000003, depc=0x00000000<\n> <\r><\n>
-
I wish the chirp had been designed with an atmega328p. Cost and footprint for the smd is similar I believe
-
Its not "that" bad .. the Attiny44A is compatible with Arduino thanks to https://github.com/SpenceKonde/ATTinyCore
For now I got Humidity and Voltage(VCC) running up fine.
-
@cimba007 Could you please share your latest working code for generating the packet?
I've created an ATTiny841 node with the RFM69CW radio (with a modified RFM69 library to match the radio config registers of MySensors) but the Raspberry Pi gateway is seeing the packets different.
Here is the sending part:
byte test[] = { 1, // last 1, // sender 255, // destination 0b00001010, // version_length: 2 bit - Protocol version (2), 1 bit - Signed flag (no), 5 bit - Length of payload (1 byte) 0b00100001, // command_ack_payload: 3 bit - Command type (C_SET), 1 bit - Request an ack (no), 1 bit - Is ack message (no), 3 bit - Payload data type (P_BYTE) 16, // type: V_TRIPPED 3, // sensor ID 1 // Value: 1 }; radio.send(255, packet, strlen(packet));
which is received as:
DEBUG TSF:MSG:READ,10-255-33,s=0,c=3,t=1,pt=0,l=2,sg=0: DEBUG !TSF:MSG:LEN,6!=9
where none of the fields match the packet
Here is the packet definition in core/MyMessage.h:
uint8_t last; ///< 8 bit - Id of last node this message passed uint8_t sender; ///< 8 bit - Id of sender node (origin) uint8_t destination; ///< 8 bit - Id of destination node /** * 2 bit - Protocol version<br> * 1 bit - Signed flag<br> * 5 bit - Length of payload */ uint8_t version_length; /** * 3 bit - Command type<br> * 1 bit - Request an ack - Indicator that receiver should send an ack back<br> * 1 bit - Is ack message - Indicator that this is the actual ack message<br> * 3 bit - Payload data type */ uint8_t command_ack_payload; uint8_t type; ///< 8 bit - Type varies depending on command uint8_t sensor; ///< 8 bit - Id of sensor that this message concerns. /* * Each message can transfer a payload. We add one extra byte for string * terminator \0 to be "printable" this is not transferred OTA * This union is used to simplify the construction of the binary data types transferred. */ union { uint8_t bValue; ///< unsigned byte value (8-bit) uint16_t uiValue; ///< unsigned integer value (16-bit) int16_t iValue; ///< signed integer value (16-bit) uint32_t ulValue; ///< unsigned long value (32-bit) int32_t lValue; ///< signed long value (32-bit) struct { //< Float messages float fValue; uint8_t fPrecision; ///< Number of decimals when serializing }; struct { //< Presentation messages uint8_t version; ///< Library version uint8_t sensorType; ///< Sensor type hint for controller, see table above }; char data[MAX_PAYLOAD + 1]; ///< Buffer for raw payload data } __attribute__((packed)); ///< Doxygen will complain without this comment
I'll try and log the raw incoming packet on the Pi side just to confirm that the radio config is actually matching.
-
Turns out that the RFM69 arduino library was missing two additional bytes from the packet header --
rfm69_header_t.version
andrfm69_header_t.sequenceNumber
inRFM69::sendFrame()
(see the diff).select(); SPI.transfer(REG_FIFO | 0x80); // Select the FIFO write register. SPI.transfer(bufferSize + 5); // rfm69_header_t.packetLen SPI.transfer(toAddress); // rfm69_header_t.recipient SPI.transfer(1); // RFM69_PACKET_HEADER_VERSION = (1u) rfm69_header_t.version header version (20180128tk: >=3.0.0 fused with controlFlags) SPI.transfer(_address); // rfm69_header_t.sender SPI.transfer(CTLbyte); // rfm69_header_t.controlFlags SPI.transfer(0); // rfm69_header_t.sequenceNumber for (uint8_t i = 0; i < bufferSize; i++) SPI.transfer(((uint8_t*) buffer)[i]); unselect();
So after adding
SPI.transfer(1);
asrfm69_header_t.version
andSPI.transfer(0);
asrfm69_header_t.sequenceNumber
the packets are now being parsed correctly:DEBUG TSF:MSG:READ,1-1-255,s=3,c=1,t=16,pt=1,l=1,sg=0:1 DEBUG TSF:MSG:BC
-
@kasparsd I'm also interested in using ATTiny with a RFM69CW module as a MySensor node. How have you initialized the radio with your modified library? Can you show a complete example?
-
@cimba007
Can you post the complete code of the chirp including the headers?
Did you only include these libraries:
#include <SPI.h>
#include "nRF24L01.h"
#include "RF24.h"
?