Modmata  v0
An arduino communications server using Modbus
Functions.cpp
Go to the documentation of this file.
1 /*
2 Modmata Functions
3 Copyright © 2023 char* teamName <shutche@siue.edu>
4 Licensed under LGPL-2.1
5 */
6 
7 /**
8  * @file Functions.cpp
9  * @author Sam Hutcherson, Chase Wallendorff, Iris Astrid
10  * @brief Define standard callback functions to control Arduino I/O
11  * @date 2023-04-06
12  */
13 
14 #include "Functions.h"
15 
16 /** @brief Current number of attached servos */
17 int servo_count = 0;
18 
19 /** @brief Array to control attached servos */
20 Servo servos[14]; // FIXME: Change to pointers so that pins 9/10 can be used as analog when Servos aren't used
21 
22 /** @brief Singleton to represent the current SPI connection settings */
23 struct spi_settings settings;
24 
25 /**
26  * @brief Helper struct that is returned directly when a callback function has no return values,
27  * and that is copy-constructed from when a clean struct is needed to populate with values
28  */
29 const registers VOID_STRUCT{0, nullptr};
30 
31 /**
32  * @brief Change the settings of the Arduino I/O pins
33  *
34  * @param argc The number of arguments contained within the 'argv' array (2)
35  * @param argv The arguments to use within the function (pin #, mode)
36  * @return void (empty struct)
37  */
38 struct registers pinMode(uint8_t argc, uint8_t *argv) {
39  if (argc == 2) {
40  pinMode(argv[0], argv[1]);
41  }
42 
43  return VOID_STRUCT;
44 }
45 
46 /**
47  * @brief Write a digital (HIGH/LOW) value to the Arduino I/O pins
48  *
49  * @param argc The number of arguments contained within the 'argv' array (2)
50  * @param argv The arguments to use within the function (pin #, value)
51  * @return void (empty struct)
52  */
53 struct registers digitalWrite(uint8_t argc, uint8_t *argv) {
54  if (argc == 2) {
55  digitalWrite(argv[0], argv[1]);
56  }
57 
58  return VOID_STRUCT;
59 }
60 
61 /**
62  * @brief Read a digital value (HIGH/LOW) from the Arduino I/O pins
63  *
64  * @param argc The number of arguments contained within the 'argv' array (1)
65  * @param argv The arguments to use within the function (pin #)
66  * @return struct containing the value of pin (1 uint8_t)
67  */
68 struct registers digitalRead(uint8_t argc, uint8_t *argv) {
69  struct registers result{VOID_STRUCT};
70 
71  if (argc == 1) {
72  uint8_t read_val = digitalRead(argv[0]);
73  result.count = 1;
74  result.value = (uint8_t *)malloc(sizeof(uint8_t));
75  result.value[0] = read_val;
76  }
77 
78  return result;
79 }
80 
81 /**
82  * @brief Write an analog value (0-255) to the Arduino I/O pins
83  *
84  * @param argc The number of arguments contained within the 'argv' array (3)
85  * @param argv The arguments to use within the function (pin #, 16-bit value split into 2 8-bit values)
86  * @return void (empty struct)
87  */
88 struct registers analogWrite(uint8_t argc, uint8_t *argv) {
89  if (argc == 3) {
90  analogWrite(argv[0], makeWord(argv[1], argv[2]));
91  }
92 
93  return VOID_STRUCT;
94 }
95 
96 /**
97  * @brief Read an analog value (0-1023) from the Arduino I/O pins
98  *
99  * @param argc The number of arguments contained within the 'argv' array (1)
100  * @param argv The arguments to use within the function (pin #)
101  * @return struct containing the value of pin (10-bit unsigned int contained within a uint16_t)
102  */
103 struct registers analogRead(uint8_t argc, uint8_t *argv) {
104  struct registers result{VOID_STRUCT};
105 
106  if (argc == 1) {
107  uint16_t read_val = analogRead(argv[0]);
108  result.count = 2;
109  result.value = (uint8_t *)malloc(sizeof(uint8_t) * 2);
110  result.value[0] = highByte(read_val);
111  result.value[1] = lowByte(read_val);
112  }
113 
114  return result;
115 }
116 
117 /**
118  * @brief Attach a connected servo to a control interface
119  *
120  * @param argc The number of arguments contained within the 'argv' array (1)
121  * @param argv The arguments to use within the function (pin #)
122  * @return struct containing the ServoIndex (uint8_t), I have no idea what it actually means though (undocumented on Arduino???)
123  */
124 struct registers servoAttach(uint8_t argc, uint8_t *argv) {
125  struct registers result{VOID_STRUCT};
126 
127  if (argc == 1) {
128  int pin = argv[0];
129  result.count = 1;
130  result.value = (uint8_t *)malloc(sizeof(uint8_t));
131  result.value[0] = 0;
132  if (pin >= 0 && pin <= 13 && servo_count < MAX_SERVOS) {
133  result.value[0] = servos[pin].attach(pin);
134  }
135  }
136 
137  return result;
138 }
139 
140 /**
141  * @brief Detach a servo from the the control interface
142  *
143  * @param argc The number of arguments contained within the 'argv' array (1)
144  * @param argv The arguments to use within the function (pin #)
145  * @return struct containing the boolean value of the operation's success (uint8_t)
146  */
147 struct registers servoDetach(uint8_t argc, uint8_t *argv) {
148  struct registers result{VOID_STRUCT};
149  if (argc == 1) {
150  int pin = argv[0];
151  result.count = 1;
152  result.value = (uint8_t *)malloc(sizeof(uint8_t));
153  result.value[0] = 0;
154  if (pin >= 0 && pin <= 13) {
155  servos[pin].detach();
156  result.value[0] = 1;
157  }
158  }
159 
160  return result;
161 }
162 
163 /**
164  * @brief Write a value to the connected servo
165  *
166  * @param argc The number of arguments contained within the 'argv' array (2)
167  * @param argv The arguments to use within the function (pin #, value)
168  * @return struct containing the boolean value of the operation's success (uint8_t)
169  */
170 struct registers servoWrite(uint8_t argc, uint8_t *argv) {
171  struct registers result{VOID_STRUCT};
172  if(argc == 2) {
173  int pin = argv[0];
174  int angle = argv[1];
175  result.count = 1;
176  result.value = (uint8_t *)malloc(sizeof(uint8_t));
177  result.value[0] = 0;
178  if (pin >= 0 && pin <= 13) {
179  servos[pin].write(angle);
180  result.value[0] = 1;
181  }
182  }
183 
184  return result;
185 }
186 
187 /**
188  * @brief Read a value from the connected servo
189  *
190  * @param argc The number of arguments contained within the 'argv' array
191  * @param argv The arguments to use within the function
192  * @return struct containing the last value written to the servo (uint8_t)
193  */
194 struct registers servoRead(uint8_t argc, uint8_t *argv) {
195  struct registers result{VOID_STRUCT};
196  if (argc == 1) {
197  int pin = argv[0];
198  if (pin >= 0 && pin <= 13) {
199  result.count = 1;
200  result.value = (uint8_t *)malloc(sizeof(uint8_t));
201  result.value[0] = servos[pin].read();
202  }
203  }
204 
205  return result;
206 }
207 
208 /**
209  * @brief Begin an I2C connection between the Arduino and a peripheral
210  *
211  * @param argc The number of arguments contained within the 'argv' array
212  * @param argv The arguments to use within the function
213  * @return void (empty struct)
214  */
215 struct registers wireBegin(uint8_t argc, uint8_t *argv) {
216  Wire.begin();
217  return VOID_STRUCT;
218 }
219 
220 /**
221  * @brief End an I2C connection between the Arduino and a peripheral
222  *
223  * @param argc The number of arguments contained within the 'argv' array
224  * @param argv The arguments to use within the function
225  * @return void (empty struct)
226  */
227 struct registers wireEnd(uint8_t argc, uint8_t *argv) {
228  Wire.end();
229  return VOID_STRUCT;
230 }
231 
232 /**
233  * @brief Change the clock speed settings of the I2C connection
234  *
235  * @param argc The number of arguments contained within the 'argv' array
236  * @param argv The arguments to use within the function
237  * @return void (empty struct)
238  */
239 struct registers wireSetClock(uint8_t argc, uint8_t *argv) {
240  if (argc == 4) {
241  // 4 8-bit values combine to make a 32-bit int
242  uint32_t clockspeed = makeDWord(argv[0], argv[1], argv[2], argv[3]);
243  Wire.setClock(clockspeed);
244  }
245 
246  return VOID_STRUCT;
247 }
248 
249 /**
250  * @brief Write data to the connected I2C peripheral
251  *
252  * @param argc The number of arguments contained within the 'argv' array
253  * @param argv The arguments to use within the function
254  * @return void (empty struct)
255  */
256 struct registers wireWrite(uint8_t argc, uint8_t *argv) {
257  if (argc >= 2) {
258  // Argument format: addr | reg | bytes
259  uint8_t addr = argv[0];
260  uint8_t reg = argv[1];
261 
262  Wire.beginTransmission(addr);
263  Wire.write(reg);
264  for(int i = 2; i < argc; i++) {
265  Wire.write(argv[i]);
266  }
267  Wire.endTransmission();
268  }
269 
270  return VOID_STRUCT;
271 }
272 
273 /**
274  * @brief Read data from the connected I2C peripheral
275  *
276  * @param argc The number of arguments contained within the 'argv' array
277  * @param argv The arguments to use within the function
278  * @return struct containing the bytes read (num_bytes * uint8_t)
279  */
280 struct registers wireRead(uint8_t argc, uint8_t *argv) {
281  struct registers result{VOID_STRUCT};
282 
283  if(argc == 3) {
284  // Argument format: addr | reg | num_bytes
285  uint8_t addr = argv[0];
286  uint8_t reg = argv[1];
287  uint8_t num_bytes = argv[2];
288  result.value = (uint8_t *)calloc(num_bytes, sizeof(uint8_t));
289 
290  Wire.beginTransmission(addr);
291  Wire.write(reg);
292  Wire.endTransmission();
293 
294  Wire.requestFrom(addr, num_bytes);
295  if(Wire.available() == num_bytes) {
296  result.count = num_bytes;
297  for(int i = 0; i < num_bytes; i++) {
298  result.value[i] = Wire.read();
299  }
300  }
301  }
302 
303  return result;
304 }
305 
306 /**
307  * @brief Begin a SPI connection between the Arduino and a peripheral
308  *
309  * @param argc The number of arguments contained within the 'argv' array
310  * @param argv The arguments to use within the function
311  * @return void (empty struct)
312  */
313 struct registers spiBegin(uint8_t argc, uint8_t *argv) {
314  if (argc == 0) {
315  SPI.begin();
316  settings.speed = 4000000;
317  settings.order = MSBFIRST;
318  settings.mode = SPI_MODE0;
319  }
320 
321  return VOID_STRUCT;
322 }
323 
324 /**
325  * @brief Change the settings of the SPI connection
326  *
327  * @param argc The number of arguments contained within the 'argv' array
328  * @param argv The arguments to use within the function
329  * @return void (empty struct)
330  */
331 struct registers spiSettings(uint8_t argc, uint8_t *argv) {
332  if (argc == 6) {
333  uint32_t clockspeed = makeDWord(argv[0], argv[1], argv[2], argv[3]);
334  settings.speed = clockspeed;
335  settings.order = argv[4];
336  settings.mode = argv[5];
337  }
338 
339  return VOID_STRUCT;
340 }
341 
342 /**
343  * @brief Exchange data over the SPI connection (Read + Write)
344  *
345  * @param argc The number of arguments contained within the 'argv' array
346  * @param argv The arguments to use within the function
347  * @return struct containing the response to the command written (argc - 1 * uint8_t)
348  */
349 struct registers spiTransferBuf(uint8_t argc, uint8_t *argv) {
350  // Register format:
351  // | CMD/ARGC | Bytes |
352  // |<-1 word->|<-31 words / 62 bytes->|
353 
354  struct registers result{VOID_STRUCT};
355 
356  if (argc > 1) {
357  uint8_t CS_pin = argv[0];
358 
359  result.count = argc - 1;
360  result.value = (uint8_t *)malloc(sizeof(uint8_t) * result.count);
361 
362  SPI.beginTransaction(SPISettings(settings.speed, settings.order, settings.mode));
363  digitalWrite((uint8_t)CS_pin, (uint8_t)LOW);
364  for(int i = 1; i < argc; i++) {
365  result.value[i-1] = SPI.transfer(argv[i]);
366  }
367  digitalWrite((uint8_t)CS_pin, (uint8_t)HIGH);
368  SPI.endTransaction();
369  }
370 
371  return result;
372 }
373 
374 /**
375  * @brief End a SPI connection between the Arduino and a peripheral
376  *
377  * @param argc The number of arguments contained within the 'argv' array (0)
378  * @param argv The arguments to use within the function (None)
379  * @return void (empty struct)
380  */
381 struct registers spiEnd(uint8_t argc, uint8_t *argv) {
382  if (argc == 0) {
383  SPI.end();
384  }
385 
386  return VOID_STRUCT;
387 }
388 
struct registers pinMode(uint8_t argc, uint8_t *argv)
Change the settings of the Arduino I/O pins.
Definition: Functions.cpp:38
struct registers spiSettings(uint8_t argc, uint8_t *argv)
Change the settings of the SPI connection.
Definition: Functions.cpp:331
struct registers wireSetClock(uint8_t argc, uint8_t *argv)
Change the clock speed settings of the I2C connection.
Definition: Functions.cpp:239
struct registers wireRead(uint8_t argc, uint8_t *argv)
Read data from the connected I2C peripheral.
Definition: Functions.cpp:280
struct registers servoRead(uint8_t argc, uint8_t *argv)
Read a value from the connected servo.
Definition: Functions.cpp:194
struct registers wireWrite(uint8_t argc, uint8_t *argv)
Write data to the connected I2C peripheral.
Definition: Functions.cpp:256
struct registers wireEnd(uint8_t argc, uint8_t *argv)
End an I2C connection between the Arduino and a peripheral.
Definition: Functions.cpp:227
struct registers spiBegin(uint8_t argc, uint8_t *argv)
Begin a SPI connection between the Arduino and a peripheral.
Definition: Functions.cpp:313
struct registers analogWrite(uint8_t argc, uint8_t *argv)
Write an analog value (0-255) to the Arduino I/O pins.
Definition: Functions.cpp:88
struct spi_settings settings
Singleton to represent the current SPI connection settings.
Definition: Functions.cpp:23
struct registers servoWrite(uint8_t argc, uint8_t *argv)
Write a value to the connected servo.
Definition: Functions.cpp:170
struct registers servoDetach(uint8_t argc, uint8_t *argv)
Detach a servo from the the control interface.
Definition: Functions.cpp:147
struct registers servoAttach(uint8_t argc, uint8_t *argv)
Attach a connected servo to a control interface.
Definition: Functions.cpp:124
struct registers analogRead(uint8_t argc, uint8_t *argv)
Read an analog value (0-1023) from the Arduino I/O pins.
Definition: Functions.cpp:103
struct registers digitalRead(uint8_t argc, uint8_t *argv)
Read a digital value (HIGH/LOW) from the Arduino I/O pins.
Definition: Functions.cpp:68
const registers VOID_STRUCT
Helper struct that is returned directly when a callback function has no return values,...
Definition: Functions.cpp:29
int servo_count
Current number of attached servos.
Definition: Functions.cpp:17
struct registers spiTransferBuf(uint8_t argc, uint8_t *argv)
Exchange data over the SPI connection (Read + Write)
Definition: Functions.cpp:349
struct registers digitalWrite(uint8_t argc, uint8_t *argv)
Write a digital (HIGH/LOW) value to the Arduino I/O pins.
Definition: Functions.cpp:53
struct registers spiEnd(uint8_t argc, uint8_t *argv)
End a SPI connection between the Arduino and a peripheral.
Definition: Functions.cpp:381
struct registers wireBegin(uint8_t argc, uint8_t *argv)
Begin an I2C connection between the Arduino and a peripheral.
Definition: Functions.cpp:215
Servo servos[14]
Array to control attached servos.
Definition: Functions.cpp:20
Header file for 'Functions.cpp'.
uint8_t mode
Definition: Functions.h:82
uint8_t * value
Definition: Functions.h:64
#define makeDWord(i0, i1, i2, i3)
Combine four 8-bit integral types into one 32-bit integral type, or in simpler terms,...
Definition: Functions.h:28
bool order
Definition: Functions.h:79
uint8_t count
Definition: Functions.h:61
uint32_t speed
Definition: Functions.h:76
A data structure to describe function arguments and return values.
Definition: Functions.h:59
A data structure to describe a SPI connection's configuration settings. Essentially the same as Ardui...
Definition: Functions.h:74