RAKWireless RUI3 RAKstar

Crazy idea to monitor your washing machine and know when it has finished.

Our washing machine is in the garage and often we forgot that we had it started, that it already finished and we should hang our laundry for drying. I was thinking about a monitoring system to know when the washing machine has finished its job and had some ideas:
(1) measure the current supplied to the machine
(2) put a sensor on the power light of the machine
(3) (that's the crazy idea) measure the vibrations of the machine and when it is no longer shaking, we know it has finished

I went for the idea (3), because it is the easiest to add to the washing machine without cutting any cables or put light sensors on it.


The application offers at the moment to setup two parameters via AT commands:

ATC+SENDINT=xxxxx => Allows to setup the heartbeat (I am alive) sending interval in xxxxx seconds.

ATC+TOUT=xxxx => Allows to setup the timeout for "off" status in seconds. For the washing machine example, I set this to 30 seconds before the application reports "inactivity".

TODO:

Add setup for the sensitivity of the vibration. At the moment this is fixed to 1/8th of the range of 2G. This makes it very sensitive.


Code sections

INFORMATION

The code snippets below are simplified. Check the provided source code for the full code.

LoRa/LoRaWAN callbacks

There are different system callbacks that are invoked on LoRa/LoRaWAN events.

For LoRaWAN there are 3 callbacks for join, send and receive events.

LoRaWAN join event is called when a join request was successful or failed. If it fails, it is called after all retries are used up, not on each single retry. The callback has the parameter _int32_t status_. The status is based on low level LoRaMAC stack results, at the moment the only usuable check is whether the status is 0 ==> join success or <>0 ==> join failed. It is possible to restart the join request from this callback if the attempt failed before.

void joinCallback(int32_t status)
{
    if (status != 0)
    {
        MYLOG("JOIN-CB", "LoRaWan OTAA - join fail! \r\n");
    }
    else
    {
        MYLOG("JOIN-CB", "LoRaWan OTAA - joined! \r\n");
    }
}

LoRaWAN TX finished callback is an important callback. It tells you whether the transmission of a LoRaWAN packet is finished. It is important, because a TX finished event is waking up the device, and when using the loop() function, a sleep call should be issued AFTER the TX finished event. The callback has the parameter _int32_t status_. The status is based on low level LoRaMAC stack results, at the moment the only usuable check is whether the status is 0 ==> send success or <>0 ==> send failed.

void sendCallback(int32_t status)
{
    MYLOG("TX-CB", "TX status %d", status);
}

LoRaWAN RX callback is called when a data packet was received from the LoRaWAN server. The callback has as parameter a pointer to a structure with information about the received data and pointers to the payload and its length.

void receiveCallback(SERVICE_LORA_RECEIVE_T *data)
{
    MYLOG("RX-CB", "RX, port %d, DR %d, RSSI %d, SNR %d", data->Port, data->RxDatarate, data->Rssi, data->Snr);
}

LoRa P2P TX finished callback is an important callback. It tells you whether the transmission of a LoRa packet is finished. It is important, because a TX finished event is waking up the device, and when using the loop() function, a sleep call should be issued AFTER the TX finished event. The callback has the no parameters, as LoRa P2P does not have any protocol that would allow to know if the sent packet was received by another node..

void send_cb(void)
{
    MYLOG("TX-P2P-CB", "P2P TX finished");
}

LoRa P2P RX callback is called when a data packet was received from another LoRa node. The callback has as parameter a structure with information about the received data, the payload and its length.

void recv_cb(rui_lora_p2p_recv_t data)
{
    MYLOG("RX-P2P-CB", "P2P RX, RSSI %d, SNR %d", data.Rssi, data.Snr);
}

setup() and loop()

setup() is used as known from Arduino sketches to setup the system. It is called once after reboot or power up.
There are two important parts in the setup() code.

First it checks whether the system is working in LoRaWAN mode or as a LoRa P2P mode. Depending on this, the different callbacks are setup.

    // Setup for LoRaWAN
    if (api.lorawan.nwm.get() == 1)
    {
        // Setup the callbacks for joined and send finished
        api.lorawan.registerRecvCallback(receiveCallback);
        api.lorawan.registerSendCallback(sendCallback);
        api.lorawan.registerJoinCallback(joinCallback);
    }
    else // Setup for LoRa P2P
    {
        api.lorawan.registerPRecvCallback(recv_cb);
        api.lorawan.registerPSendCallback(send_cb);
    }

Second a periodic timer is initialized to wake up the system in intervals to send a packet to the LoRaWAN server or other LoRa P2P nodes. The interval time is set with the variable g_send_repeat_time.

    // Create a timer.
    api.system.timer.create(RAK_TIMER_0, sensor_handler, RAK_TIMER_PERIODIC);
    if (g_send_repeat_time != 0)
    {
        // Start a timer.
        api.system.timer.start(RAK_TIMER_0, g_send_repeat_time, NULL);
    }

The loop() function does nothing beside of killing itself, which prevents that it is called frequently from the underlaying RUI3 scheduler.

void loop()
{
    api.system.scheduler.task.destroy();
}

send_handler & send_packet

This functions are where the action is happening.

send_handler is called by the timer. First, if in LoRaWAN mode, it checks whether the node has already joined the network. Then, in this example, it is just creating a dummy payload. But this can be extended for example to read data from sensors and add these values to the payload.
After the payload is ready, it calls send_packet to get the packet sent out.

void sensor_handler(void *)
{
    if (api.lorawan.nwm.get() == 1)
    { // Check if the node has joined the network
        if (!api.lorawan.njs.get())
        {
            MYLOG("UPLINK", "Not joined, skip sending");
            return;
        }
    }

    // Create payload
    char value = 0x31;
    for (int idx = 0; idx < 4; idx++)
    {
        g_solution_data[idx] = value;
        value++;
    }

    // Send the packet
    send_packet();
}

send_packet is checking whether the node is in LoRaWAN or LoRa P2P mode and sends the created payload depending on the selected mode.

void send_packet(void)
{
    // Check if it is LoRaWAN
    if (api.lorawan.nwm.get() == 1)
    {
        // Send the packet
        if (api.lorawan.send(4, g_solution_data, set_fPort, g_confirmed_mode, g_confirmed_retry))
        {
            MYLOG("UPLINK", "Packet enqueued, size 4");
        }
        else
        {
            MYLOG("UPLINK", "Send failed");
        }
    }
    // It is P2P
    else
    {
        MYLOG("UPLINK", "Send packet with size 4 over P2P");
        if (api.lorawan.psend(4, g_solution_data))
        {
            MYLOG("UPLINK", "Packet enqueued");
        }
        else
        {
            MYLOG("UPLINK", "Send failed");
        }
    }
}

Get RUI3 devices

Get a RAKwireless RUI3 WisDuo stamp module, breakout board or evaluation board from our store

Get a RAKwireless RUI3 WisBlock Core module from our store

INFORMATION

Only the following modules are supported by RUI3:

WisDuo

WisBlock Core


LoRa® is a registered trademark or service mark of Semtech Corporation or its affiliates.


LoRaWAN® is a licensed mark.