Waveshare 8ch Relay Board Example
A simple repository containing (functioning) examples of how to use the Waveshare 8ch relay board.
channel_select.cpp
Go to the documentation of this file.
1 /********************************************
2  Channel Select in C++
3  * *
4  * Copyright © Simon Cahill and *
5  * contributors *
6  ********************************************/
7 
8 //////////////////////////////////
9 // System Includes //
10 //////////////////////////////////
11 #include <algorithm>
12 #include <iostream>
13 #include <fstream>
14 #include <regex>
15 #include <string>
16 
17 #include <stdint.h>
18 #include <sys/stat.h>
19 #include <unistd.h>
20 
21 //////////////////////////////////
22 // Usings //
23 //////////////////////////////////
24 using std::cerr;
25 using std::cout;
26 using std::endl;
27 using std::regex;
28 using std::regex_search;
29 using std::string;
30 using std::tolower;
31 using std::transform;
32 
33 //////////////////////////////////
34 // Global Variables //
35 //////////////////////////////////
36 static uint32_t g_channel = 0; //!< The selected channel to enable and/or toggle
37 static bool g_state = false; //!< The desired state for the channel (on/off)
38 
39 /**
40  * @brief A simple array representing a map to the channels on the relay board, corresponding to
41  * the GPIO pings.
42  *
43  * E.g.: CHANNELS[0] = channel 1 = GPIO5
44  */
45 static const uint32_t CHANNELS[8] {
46  5, 6, 13, 16, 19, 20, 21, 26
47 };
48 
49 /**
50  * @brief Simple string formatter template function.
51  *
52  * @tparam Args The arguments to be formatted into the string
53  *
54  * @param format The C-style format string
55  * @param args Varargs list of arguments
56  *
57  * @return string The formatted string.
58  */
59 template<typename... Args>
60 string formatString(const string& format, Args... args) {
61  auto stringSize = snprintf(NULL, 0, format.c_str(), args...) + 1; // +1 for \0
62  std::unique_ptr<char[]> buffer(new char[stringSize]);
63 
64  snprintf(buffer.get(), stringSize, format.c_str(), args...);
65 
66  return string(buffer.get(), buffer.get() + stringSize - 1); // std::string handles termination for us.
67 }
68 
69 //////////////////////////////////
70 // Function Prototypes //
71 //////////////////////////////////
72 bool directoryExists(string dirPath); //!< Whether or not a given directory exists or not
73 bool parseArgs(int32_t argC, char** argV); //!< Parses command-line arguments passed to the application
74 
75 void enableGpio(); //!< Enables a given GPIO pin
76 void setGpio(); //!< Sets a GPIO pin on/off, depending on the desired state
77 void showHelp(); //!< Prints the help text
78 
79 int main(int32_t argC, char** argV) {
80 
81  if (argC <= 2) {
82  showHelp();
83  return -1;
84  }
85 
86  if (!parseArgs(argC, argV)) return -1;
87 
88  return 0;
89 }
90 
91 /**
92  * @brief Determines whether or not a given directory exists.
93  *
94  * @param dirPath The path to check.
95  *
96  * @return true If the directory exists.
97  * @return false Otherwise.
98  */
99 bool directoryExists(string dirPath) {
100  struct stat _stat;
101  if (stat(dirPath.c_str(), &_stat) == 0 && S_ISDIR(_stat.st_mode)) {
102  return true;
103  }
104  return false;
105 }
106 
107 /**
108  * @brief Parses arguments passed to the application and sets the required parameters accordingly.
109  *
110  * @param argC The total arg count
111  * @param argV A pointer-pointer to the arg list
112  *
113  * @return true If the application should stay alive
114  * @return false Otherwise
115  */
116 bool parseArgs(int32_t argC, char** argV) {
117  const static regex CHANNEL_PATTERN(R"((ch)?[0-8])");
118  const static regex STATE_PATTERN(R"(on|off|true|false)");
119 
120  for (int32_t i = 0; i < argC; i++) {
121  string arg = argV[i];
122 
123  // Convert arg to lower
124  transform(arg.begin(), arg.end(), arg.begin(), [&](const auto c) { return tolower(c); });
125 
126  if (regex_search(arg, CHANNEL_PATTERN)) {
127  g_channel = std::stoi(arg.substr(arg.size() - 1, 1));
128  } else if (regex_search(arg, STATE_PATTERN)) {
129  g_state = (arg == "on" || arg == "true");
130  } else {
131  showHelp();
132  return false;
133  }
134  }
135 
136  return true;
137 }
138 
139 /**
140  * @brief Enables use of the desired GPIO pin
141  */
142 void enableGpio() {
143  static const string GPIO_DIR_PATH = "/sys/class/gpio/gpio";
144 
145  const auto gpioPath = formatString("%s%d", GPIO_DIR_PATH.c_str(), g_channel);
146 
147  if (directoryExists(gpioPath)) return;
148 
149  // Make sure Linux exports the pin
150  {
151  std::ofstream fileStream("/sys/class/gpio/export", std::ios::trunc);
152  fileStream << g_channel << endl;
153  }
154 
155  // Set the GPIO direction
156  {
157  std::ofstream fileStream(formatString("%s/direction", gpioPath.c_str()), std::ios::trunc);
158  fileStream << "out" << endl;
159  }
160 }
161 
162 /**
163  * @brief Sets the desired GPIO pin either on or off, depending on cmd-args.
164  */
165 void setGpio() {
166  static const string GPIO_DIR_PATH = "/sys/class/gpio/gpio";
167 
168  const auto gpioPath = formatString("%s%d/value", GPIO_DIR_PATH.c_str(), g_channel);
169 
170  std::ofstream fileStream(gpioPath, std::ios::trunc);
171  fileStream << static_cast<int32_t>(g_state) << endl;
172 }
173 
174 /**
175  * @brief Prints the help text to the console.
176  */
177 void showHelp() {
178  cout << R"(
179 Usage:
180  channel_select [ch]0-8 on/off/true/false
181  channel_select --help
182  )"<<endl;
183 }