/      日本語

Universal Receiver

Receives various types of packets

By running NWK_LAYERED on twe_twelite.network and NWK_SIMPLE on twe_twelite.network2 in the MWX library, you can receive and interpret various types of packets, including Layered Tree Network packets (such as TWELITE PAL, ARIA).

However, radio packets must be on the same channel and have the same application ID.

main.cpp

setup(), loop(), and the received packet callback function on_rx_packet() are described.

setup()

These objects are declared in pkt_handler.cpp and initialized with pnew() during setup(). They mainly interpret the packet payload (data).

	mwx::pnew(g_pkt_pal);
	mwx::pnew(g_pkt_apptwelite);
	mwx::pnew(g_pkt_actsamples);
	mwx::pnew(g_pkt_unknown);

Two network objects are created. Always set the_twelite.network to NWK_LAYERED.

	auto&& nwk_ly = the_twelite.network.use<NWK_LAYERED>();
	auto&& nwk_sm = the_twelite.network2.use<NWK_SIMPLE>();

loop()

In this sample, the important process in loop() is the .refresh() operation performed approximately every 1 second. Only g_pkt_apptwelite().refresh() performs the duplicate checker timeout processing. The other objects do nothing.

	if (TickTimer.available()) {
		static unsigned t;
		if (!(++t & 0x3FF)) {
			g_pkt_pal.refresh();
			g_pkt_apptwelite.refresh();
			g_pkt_actsamples.refresh();
			g_pkt_unknown.refresh();
		}
	}

on_rx_packet()

void on_rx_packet(packet_rx& rx, bool_t &handled) {
	auto type = rx.get_network_type();
	bool b_handled = false;

	// PAL
	if (!b_handled
		&& type == mwx::NETWORK::LAYERED
		&& g_pkt_pal.analyze(rx, b_handled)
	) {
		g_pkt_pal.display(rx);
	}

	// Act samples
	if (!b_handled
		&& type == mwx::NETWORK::SIMPLE
		&& g_pkt_actsamples.analyze(rx, b_handled)
	) {
		g_pkt_actsamples.display(rx);
	}

	// Standard application (e.g. App_Twelite)
	if (!b_handled
		&& type == mwx::NETWORK::NONE
		&& g_pkt_apptwelite.analyze(rx, b_handled)
	) {
		g_pkt_apptwelite.display(rx);
	}

	// unknown
	if (!b_handled) {
		g_pkt_unknown.analyze(rx, b_handled);
		g_pkt_unknown.display(rx);
	}
}

This is the most important part of this sample code. The packet type is determined by auto type = rx.get_network_type();.

  • mwx::NETWORK::LAYERED : Packet of NWK_LAYERED Layered Tree Network
  • mwx::NETWORK::SIMPLE : Packet of NWK_SIMPLE
  • mwx::NETWORK::NONE : No network (e.g. App_Twelite)
  • Others : Error or unsupported packets

In the case of mwx::NETWORK::NONE, duplicate checker processing for the same packet that may be sent multiple times due to retransmissions is not performed internally by the MWX library. You need to implement these yourself. This sample provides dup_checker.hpp and dup_checker.cpp.

Packet interpretation refers to the packet_rx& object which wraps tsRxDataApp*. The packet_rx class itself has no special functions; it only defines access methods to some information obtained from tsRxDataApp* using get_psRxDataApp().

pkt_common.hpp

Defined to unify the interface of the packet interpretation part.

template <class D>
struct pkt_handler {
	D& self() { return static_cast<D&>(*this); }
	bool analyze(packet_rx& rx, bool &b_handled) {
		return self().pkt.analyze(rx, b_handled);
	}
	void display(packet_rx& rx) {
		Serial
			<< crlf
			<< format("!PKT_%s(%03d-%08x/S=%d/L=%03d/V=%04d)"
					, self().get_label_packet_type()
					, self().pkt.data.u8addr_src
					, self().pkt.data.u32addr_src
					, rx.get_psRxDataApp()->u8Seq
					, rx.get_lqi()
					, self().pkt.data.u16volt
					);

		self().disp_detail(rx);
	}
	void refresh() {
		self()._refresh();
	}
};

// packet analyzer for App_Twelite
class pkt_handler_apptwelite : public pkt_handler<pkt_handler_apptwelite> {
	friend class pkt_handler<pkt_handler_apptwelite>;
	pkt_apptwelite pkt;
	void disp_detail(packet_rx& rx);
	const char* get_label_packet_type() { return "AppTwelite"; }
	void _refresh() { pkt.refresh(); }
public:
	pkt_handler_apptwelite() : pkt() {}
};
  • analyze() : Interpret the packet payload.
  • display() : Display packet information.
  • refresh() : Describes processing every 1 second.
  • self() : Casts to derived class D.

Furthermore, the packet interpretation class (e.g. pkt_handler_apptwelite above) contains a member object pkt. The actual packet interpretation part is implemented in each analyze() in pkt_???.cpp.

pkt_???.hpp, pkt_???.cpp

Packet interpretation parts for each packet type define analyze() and data structure data. The member data is a struct inheriting the common struct PktDataCommon. Using this common part, the code for serial output of packet data is succinctly written.

pkt_pal

Supports packets related to PAL. PAL packet structure has a complex data structure. Here, an implementation using EASTL containers is done.

  • _vect_pal_sensors : Pool of _pal_sensor objects. This object is a dedicated class used for intrusive map.
  • _map_pal_sensors : Intrusive map structure for efficient search of sensor data.

Each time multiple data in one packet are added, entries are secured in _vect_pal_sensors and values are stored. When all data in the packet are interpreted, _map_pal_sensors keyed by sensor type is constructed.

dup_checker

Implements duplicate checker. The checker’s behavior can be customized by template parameters.

Template parameters

  • MODE : Specifying MODE_REJECT_SAME_SEQ excludes packets with the same sequence number. Used when packet order is rearranged. MODE_REJECT_OLDER_SEQ adopts the newer number.
  • TIMEOUT_ms : Interval for initializing the duplicate database. Specifying 1000 means data older than 1 second are erased. Packets previously excluded can be accepted again after the database is initialized.
  • N_ENTRIES : Maximum number of elements allocated in the data structure.
  • N_BUCKET_HASH : Maximum number of hash values. Specify a prime number. Decided based on the types of wireless nodes received.

Containers

  • _mmap_entries : Intrusive hash multi-map structure. Search key is the wireless node serial number.
  • _vect_pool : Fixed number (N_ENTRIES) of elements allocated for use in the map structure.
  • _ring_vecant_idx : Manages indexes of elements in _vect_pool not used by _mmap_entries. It is a ring buffer structure; when adding elements, one value is taken from the ring buffer, and when removing, it is returned.

Duplicate check

	bool check_dup(uint32_t u32ser, uint16_t u16val, uint32_t u32_timestamp) {
		// find entry by key:u32ser.
		auto r = _mmap_entries.equal_range(u32ser);

        ...
    }

To search data from the multi-map structure, call .equal_range(). The obtained r is an iterator enumerating elements with the same serial number.

Each element (_dup_checker_entry) records timestamp and sequence number. Duplicate checking is done based on these values.