// Copyright (C)2016-2024, Philip Munts dba Munts Technologies.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
using Microsoft.SmallBasic.Library;
using System;
using System.Net;
using org.acplt.oncrpc;
namespace GPIO
{
///
/// The GPIO object allows you to send commands to a MuntsOS GPIO Thin Server.
///
[SmallBasicType]
public static class GPIO
{
private static gpio_server_oncrpcClient server = null;
///
/// Open a connection to the specified GPIO server.
///
/// Server domain name or IP address.
public static void Connect(Primitive servername)
{
IPAddress serveraddress;
// Check for invalid parameter
if (Uri.CheckHostName((string)servername) == UriHostNameType.Unknown)
{
throw new GPIOException("GPIO server name is invalid");
}
// Resolve the server IP address
try
{
serveraddress = Dns.GetHostAddresses((string)servername)[0];
}
catch
{
throw new GPIOException("GPIO server name cannot be resolved");
}
try
{
// Create RPC client object
server = new gpio_server_oncrpcClient(serveraddress, OncRpcProtocols.ONCRPC_UDP);
// Reduce timeout to 2 seconds
OncRpcUdpClient c = (OncRpcUdpClient)server.GetClient();
c.setTimeout(2000);
c.setRetransmissionTimeout(500);
}
catch
{
throw new GPIOException("GPIO server handle failed");
}
}
///
/// Convert a Small Basic Primitive value to GPIO data direction
///
/// Data direction: "input", "output", 0, or 1.
///
private static int ToDirection(Primitive p)
{
string s = (string)p;
if (String.Equals(s, "input", StringComparison.OrdinalIgnoreCase) ||
String.Equals(s, "0"))
return GPIO_DIRECTION.GPIO_DIRECTION_INPUT;
if (String.Equals(s, "output", StringComparison.OrdinalIgnoreCase) ||
String.Equals(s, "1"))
return GPIO_DIRECTION.GPIO_DIRECTION_OUTPUT;
throw new GPIOException("Invalid value for GPIO data direction");
}
///
/// Convert a Small Basic Primitive value to GPIO logic level
///
/// Logic level: "true", "false", "high", "low", "on", off", 1, or 0.
///
private static int ToLogicLevel(Primitive p)
{
string s = (string)p;
if (String.Equals(s, "true", StringComparison.OrdinalIgnoreCase) ||
String.Equals(s, "high", StringComparison.OrdinalIgnoreCase) ||
String.Equals(s, "on", StringComparison.OrdinalIgnoreCase) ||
String.Equals(s, "1"))
return 1;
if (String.Equals(s, "false", StringComparison.OrdinalIgnoreCase) ||
String.Equals(s, "low", StringComparison.OrdinalIgnoreCase) ||
String.Equals(s, "off", StringComparison.OrdinalIgnoreCase) ||
String.Equals(s, "0"))
return 0;
throw new GPIOException("Invalid value for GPIO logic level");
}
///
/// Configure a GPIO pin
///
/// Pin number.
/// Data direction: "input" or "output". The values 0 and 1 are also allowed.
/// Initial output logic level: "true" or "false". The values "high", "low", "on", "off", 1 and 0 are also allowed. Ignored for input pins.
public static void Configure(Primitive pin, Primitive direction, Primitive state)
{
int p = (int)pin;
int d = ToDirection(direction);
int s = ToLogicLevel(state);
int status;
try
{
status = server.gpio_open_1(p, d, s);
}
catch
{
throw new GPIOException("RPC gpio_open_1() failed");
}
if (status < 0)
{
throw new GPIOException("RPC gpio_open_1() returned error " + status.ToString() + " " + p.ToString() + " " + d.ToString() + " " + s.ToString());
}
}
///
/// Read a GPIO pin
///
/// Pin number.
/// Logic level: "true" or "false".
public static Primitive Read(Primitive pin)
{
int p = (int)pin;
int status;
try
{
status = server.gpio_read_1(p);
}
catch
{
throw new GPIOException("RPC gpio_read_1() failed");
}
if (status < 0)
{
throw new GPIOException("RPC gpio_read_1() returned error " + status.ToString());
}
if (status > 1)
{
throw new GPIOException("RPC gpio_read_1() returned invalid value " + status.ToString());
}
return (Primitive)(status == 0 ? "false" : "true");
}
///
/// Write to GPIO pin
///
/// Pin number.
/// Output state: "true" or "false". The values "high", "low", "on", "off", 1 and 0 are also allowed.
public static void Write(Primitive pin, Primitive state)
{
int p = (int)pin;
int s = ToLogicLevel(state);
int status;
try
{
status = server.gpio_write_1(p, s);
}
catch
{
throw new GPIOException("RPC gpio_write_1() failed");
}
if (status < 0)
{
throw new GPIOException("RPC gpio_write_1() returned error " + status.ToString());
}
}
}
}