RS485Driver/HLApp/main.c (118 lines of code) (raw):

/* * Copyright (c) Microsoft Corporation. * Licensed under the MIT License. */ // This sample C application for Azure Sphere sends messages to, and receives // responses from, a real-time capable application running an RS-485 driver. // It sends a message every second and prints the message which was sent, // and the response which was received. // // It uses the following Azure Sphere libraries // - log (displays messages in the Device Output window during debugging) // - application (establish a connection with a real-time capable application). // - eventloop (system invokes handlers for timer events) #include <signal.h> #include <string.h> #include <stdio.h> #include <ctype.h> #include <stdbool.h> #include <errno.h> #include <unistd.h> #include <sys/time.h> #include <sys/socket.h> #include <applibs/log.h> #include <applibs/application.h> #include "../common_defs.h" #include "eventloop_timer_utilities.h" #include "rs485_hl_driver.h" /// <summary> /// Exit codes for this application. These are used for the /// application exit code. They must all be between zero and 255, /// where zero is reserved for successful termination. /// </summary> typedef enum { ExitCode_Success = 0, ExitCode_TermHandler_SigTerm = 1, ExitCode_TimerHandler_Consume = 2, ExitCode_SendMsg_Send = 3, ExitCode_SocketHandler_Recv = 4, ExitCode_Init_EventLoop = 5, ExitCode_Init_SendTimer = 6, ExitCode_Init_Connection = 7, ExitCode_Init_Rs485 = 8, ExitCode_Main_EventLoopFail = 9 } ExitCode; static EventLoop *eventLoop = NULL; static EventLoopTimer *sendTimer = NULL; static volatile sig_atomic_t exitCode = ExitCode_Success; static uint8_t rs485RxBuffer[2000]; // Handy typedef, used in SendTimerEventHandler for processing Modbus commands typedef struct { uint8_t *command; uint8_t length; } message_t; const char rtAppComponentId[] = "1CCE66F1-28E9-4DA4-AD25-D247FD362DE7"; static void TerminationHandler(int signalNumber); static void SendTimerEventHandler(EventLoopTimer *timer); static void Rs485ReceiveHandler(int bytesReceived); static ExitCode InitHandlers(void); static void CloseHandlers(void); /// <summary> /// Signal handler for termination requests. This handler must be async-signal-safe. /// </summary> static void TerminationHandler(int signalNumber) { // Don't use Log_Debug here, as it is not guaranteed to be async-signal-safe. exitCode = ExitCode_TermHandler_SigTerm; } /// <summary> /// Handle send timer event by sending a command sequence to the RS-485 driver. /// </summary> static void SendTimerEventHandler(EventLoopTimer *timer) { static int currCommand = 0; static const message_t commands[] = { { "\xff\xff\xff\xff\x80\x25\x00\x00", 8}, // Change baud rate on the RS-485 driver (Note: invert endianness in the baudrate value!) { "\x01\x04\x00\x01\x00\x01\x60\x0A", 8}, // Measure temperature { "\x01\x04\x00\x02\x00\x01\x90\x0A", 8}, // Measure humidity }; if (ConsumeEventLoopTimerEvent(timer) != 0) { exitCode = ExitCode_TimerHandler_Consume; return; } Rs485_Send(commands[currCommand].command, commands[currCommand].length); currCommand++; currCommand %= 3; } /// <summary> /// Handle receive callback from the RS-485 driver. /// The received bytes will be available in the RX buffer passed to Rs485_Init(). /// </summary> static void Rs485ReceiveHandler(int bytesReceived) { // Just re-logging the received bytes. if (bytesReceived > 0) { Log_Debug("Rs485 Callback: received %d bytes: ", bytesReceived); for (int i = 0; i < bytesReceived; ++i) { Log_Debug("%02x", rs485RxBuffer[i]); if (i != bytesReceived - 1) { Log_Debug(":"); } } Log_Debug("\n"); } return; } /// <summary> /// Set up SIGTERM termination handler and event handlers for send timer /// and to receive data from real-time capable application. /// </summary> /// <returns> /// ExitCode_Success if all resources were allocated successfully; otherwise another /// ExitCode value which indicates the specific failure. /// </returns> static ExitCode InitHandlers(void) { struct sigaction action; memset(&action, 0, sizeof(struct sigaction)); action.sa_handler = TerminationHandler; sigaction(SIGTERM, &action, NULL); eventLoop = EventLoop_Create(); if (eventLoop == NULL) { Log_Debug("Could not create event loop.\n"); return ExitCode_Init_EventLoop; } // Register a one-second timer to send a message to the real-time RS-485 driver (RTApp). static const struct timespec sendPeriod = { .tv_sec = 1, .tv_nsec = 0 }; sendTimer = CreateEventLoopPeriodicTimer(eventLoop, &SendTimerEventHandler, &sendPeriod); if (sendTimer == NULL) { return ExitCode_Init_SendTimer; } // Initialize the real-time RS-485 driver (RTApp). if (Rs485_Init(eventLoop, rs485RxBuffer, sizeof(rs485RxBuffer), Rs485ReceiveHandler) == -1) { return ExitCode_Init_Rs485; } return ExitCode_Success; } /// <summary> /// Clean up the resources previously allocated. /// </summary> static void CloseHandlers(void) { DisposeEventLoopTimer(sendTimer); Rs485_Close(); EventLoop_Close(eventLoop); } int main(void) { Log_Debug("High-level RS-485 comms application\n"); Log_Debug("Sends messages to, and receives messages from an RS-485 driver running on the RT-Core.\n"); exitCode = InitHandlers(); while (exitCode == ExitCode_Success) { EventLoop_Run_Result result = EventLoop_Run(eventLoop, -1, true); // Continue if interrupted by signal, e.g. due to breakpoint being set. if (result == EventLoop_Run_Failed && errno != EINTR) { exitCode = ExitCode_Main_EventLoopFail; } } CloseHandlers(); Log_Debug("Application exiting.\n"); return exitCode; }