Over the last couple of evenings I have been working on a .NET nanoFramework library for the RAKwireless RAK4200 module using a STM32F769I Discovery, RAK4200 Breakoutboard, Seeedstudio Grove Base Shield for Arduino and a Seeedstudio Grove-4 pin Female Jumper to Grove 4 pin Conversion Cable.
My sample code has compile time options for synchronous and asynchronous operation.
//---------------------------------------------------------------------------------
// Copyright (c) May 2022, devMobile Software
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//---------------------------------------------------------------------------------
//#define SERIAL_SYNC_READ
#define SERIAL_ASYNC_READ
//#define SERIAL_THREADED_READ
#define ST_STM32F769I_DISCOVERY // nanoff --target ST_STM32F769I_DISCOVERY --update
...
namespace devMobile.IoT.LoRaWAN.nanoFramework.RAK4200
{
using System;
using System.Diagnostics;
using System.IO.Ports;
using System.Threading;
public class Program
{
private static SerialPort _SerialPort;
#if SERIAL_THREADED_READ
private static Boolean _Continue = true;
#endif
...
#if ST_STM32F769I_DISCOVERY
private const string SerialPortId = "COM6";
#endif
public static void Main()
{
#if SERIAL_THREADED_READ
Thread readThread = new Thread(SerialPortProcessor);
#endif
Debug.WriteLine("devMobile.IoT.LoRaWAN.nanoFramework.RAK4200 BreakoutSerial starting");
Debug.Write("Ports:");
foreach (string port in SerialPort.GetPortNames())
{
Debug.Write($" {port}");
}
Debug.WriteLine("");
try
{
// set GPIO functions for COM2 (this is UART1 on ESP32)
#if ESP32_WROOM
Configuration.SetPinFunction(Gpio.IO04, DeviceFunction.COM2_TX);
Configuration.SetPinFunction(Gpio.IO05, DeviceFunction.COM2_RX);
#endif
_SerialPort = new SerialPort(SerialPortId);
// set parameters
_SerialPort.BaudRate = 115200;
_SerialPort.Parity = Parity.None;
_SerialPort.DataBits = 8;
_SerialPort.StopBits = StopBits.One;
_SerialPort.Handshake = Handshake.None;
_SerialPort.ReadTimeout = 1000;
_SerialPort.NewLine = "\r\n";
//_SerialPort.WatchChar = '\n'; // May 2022 WatchChar event didn't fire github issue https://github.com/nanoframework/Home/issues/1035
_SerialPort.Open();
_SerialPort.WatchChar = '\n';
#if SERIAL_THREADED_READ
readThread.Start();
#endif
#if SERIAL_ASYNC_READ
_SerialPort.DataReceived += SerialDevice_DataReceived;
#endif
while (true)
{
string atCommand = "at+version";
Debug.WriteLine($"TX:{atCommand} bytes:{atCommand.Length}");
_SerialPort.WriteLine(atCommand);
#if SERIAL_SYNC_READ
// Read the response
string response = _SerialPort.ReadLine();
Debug.WriteLine($"RX:{response.Trim()} bytes:{response.Length}");
#endif
Thread.Sleep(15000);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
#if SERIAL_ASYNC_READ
private static void SerialDevice_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort serialPort = (SerialPort)sender;
string response;
switch (e.EventType)
{
case SerialData.Chars:
/*
response = serialPort.ReadExisting();
if ( response.Length>0)
{
Debug.WriteLine($"RX Char:{response.Trim()} bytes:{response.Length}");
}
*/
break;
case SerialData.WatchChar:
response = serialPort.ReadExisting();
if (response.Length > 0)
{
Debug.WriteLine($"RX WatchChar :{response.Trim()} bytes:{response.Length}");
}
break;
default:
Debug.Assert(false, $"e.EventType {e.EventType} unknown");
break;
}
}
#endif
#if SERIAL_THREADED_READ
public static void SerialPortProcessor()
{
string response;
while (_Continue)
{
try
{
response = _SerialPort.ReadLine();
//response = _SerialPort.ReadExisting();
Console.WriteLine($"RX:{response} bytes:{response.Length}");
}
catch (TimeoutException ex)
{
Console.WriteLine($"Timeout:{ex.Message}");
}
}
}
#endif
}
}
When I requested the RAK4200 Module version information with “at+version” the response was a single line (unlike the RAK3172 version where the response is three lines). The asynchronous version of the application displays character(s) as they arrive so a response could be split across multiple SerialDataReceived events.
With the initial version of SerialDevice_DataReceived event handler the firmware version response was available in the first SerialData.Chars event, then a SerialData.Chars event fired for each character
I also noticed that the “SerialData.WatchChar” event was not firing. After some experimentation I found that if I set the SerialPort.WatchChar before opening the serial port there were no events, but if I set SerialPort.WatchChar after opening the serial port events were fired as expected(See note re github issue in code)
I also implemented a threaded approach for reading characters from the serial port. Normally using Exceptions for flow control is not a good idea but in this case I can’t see an alternative approach.
The RAK4200 Module defaults 115200 baud which seems overkill considering the throughput of a LoRaWAN link.