cmdInterpreter.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  1. #include "cmdInterpreter.h"
  2. #define CMD_M_MAX_LENGTH 121 // 120 channels + 1 pwm value
  3. #define CMD_N_MAX_LENGTH 240 // 120 channels + 120 pwm values
  4. // Constructor
  5. cmdInterpreter::cmdInterpreter() {
  6. this->prepareForNextCommand();
  7. this->debug = false;
  8. }
  9. /**
  10. * prepare everything for the next command
  11. */
  12. void cmdInterpreter::prepareForNextCommand(void) {
  13. memset((void *) this->cmdBuffer, 0x00, CMD_BUFFER_LENGTH);
  14. this->cmdBuffer_ptr = this->cmdBuffer;
  15. }
  16. /**
  17. * analyse command
  18. */
  19. return_code_t cmdInterpreter::interprete(pca9635 pwm_driver[], uint8_t cnt, uint8_t oe_pin) {
  20. uint8_t cmd_len = (uint8_t) strlen((char *) this->cmdBuffer);
  21. uint8_t *cmd_buf_pntr = this->cmdBuffer;
  22. cmd_buf_pntr++; // skip command identifier
  23. return_code_t returncode = RETURN_ERROR;
  24. // check if all chars are valid hex chars
  25. while (*cmd_buf_pntr) {
  26. if(*cmd_buf_pntr == CMD_CR){
  27. *cmd_buf_pntr = 68;
  28. }
  29. if (!isxdigit(*cmd_buf_pntr)) {
  30. return RETURN_ERROR;
  31. }
  32. ++cmd_buf_pntr;
  33. }
  34. cmd_buf_pntr = this->cmdBuffer; // reset pointer
  35. switch(*cmd_buf_pntr) {
  36. // set single channel PWM
  37. case 'S': {
  38. uint8_t data[2] = {0}; //parameter 0: channel, parameter 1: pwm
  39. returncode = readParameters(cmd_buf_pntr, cmd_len, data, 2);
  40. if (returncode != RETURN_CR) {
  41. return returncode;
  42. }
  43. if (data[0] >= 120) {
  44. return RETURN_ERROR;
  45. }
  46. char s[64] = {'\0'};
  47. if (this->debug) {
  48. sprintf(s, " set channel %d to %d", data[0], data[1]);
  49. doubleS.writeSerial(s);
  50. }
  51. uint8_t dev = data[0] / 10;
  52. uint8_t port = data[0] % 10;
  53. if (this->debug) {
  54. sprintf(s, " (dev: %d ch %d)", dev, port);
  55. doubleS.writeSerial(s);
  56. }
  57. if (dev < cnt && pwm_driver[dev].isAvailable()) {
  58. if (pwm_driver[dev].set_led_pwm(port, data[1])) {
  59. if (this->debug)
  60. doubleS.writeSerial(" channel is set!");
  61. } else {
  62. if (this->debug)
  63. doubleS.writeSerial(" error while setting channel!");
  64. return RETURN_ERROR;
  65. }
  66. } else {
  67. if (this->debug)
  68. doubleS.writeSerial(" dev not available!");
  69. return RETURN_ERROR;
  70. }
  71. break;
  72. }
  73. // read single channel PWM
  74. case 'R': {
  75. uint8_t data[1] = {0}; //parameter 0: channel
  76. returncode = readParameters(cmd_buf_pntr, cmd_len, data, 1);
  77. if (returncode != RETURN_CR) {
  78. return returncode;
  79. }
  80. if (data[0] >= 120) {
  81. return RETURN_ERROR;
  82. }
  83. uint8_t dev = data[0] / 10;
  84. uint8_t port = data[0] % 10;
  85. if (dev < cnt && pwm_driver[dev].isAvailable()) {
  86. uint8_t pwm = pwm_driver[dev].getPWMValue(port);
  87. char s[64] = {'\0'};
  88. sprintf(s, " pwm value of channel %d is %d (hex: %X)", data[0],pwm, pwm);
  89. doubleS.writeSerial(s);
  90. }
  91. break;
  92. }
  93. // set the output enable (1 means enable, 0 means disalble outputs)
  94. case 'O': {
  95. uint8_t data[1] = {0}; //parameter 0: value
  96. returncode = readParameters(cmd_buf_pntr, cmd_len, data, 1);
  97. if (returncode != RETURN_CR) {
  98. return returncode;
  99. }
  100. digitalWrite(oe_pin, !data[0]);
  101. break;
  102. }
  103. // set the blinking interval
  104. case 'I': {
  105. uint8_t data[1] = {0}; //parameter 0: value
  106. returncode = readParameters(cmd_buf_pntr, cmd_len, data, 1);
  107. if (returncode != RETURN_CR) {
  108. return returncode;
  109. }
  110. char s[64] = {'\0'};
  111. if (this->debug) {
  112. sprintf(s, " set group interval to : %d (%dms)", data[0], (416*(data[0]+1))/10);
  113. doubleS.writeSerial(s);
  114. }
  115. for (uint8_t i = 0; i < cnt; i++) {
  116. if (pwm_driver[i].isAvailable()) {
  117. if (pwm_driver[i].set_group_blinking(data[0])) {
  118. if (this->debug)
  119. doubleS.writeSerial(" set!");
  120. } else {
  121. if (this->debug)
  122. doubleS.writeSerial(" error while setting!");
  123. return RETURN_ERROR;
  124. }
  125. }
  126. }
  127. break;
  128. }
  129. // set the blinking dutycycle
  130. case 'i': {
  131. uint8_t data[1] = {0}; //parameter 0: value
  132. returncode = readParameters(cmd_buf_pntr, cmd_len, data, 1);
  133. if (returncode != RETURN_CR) {
  134. return returncode;
  135. }
  136. char s[64] = {'\0'};
  137. if (this->debug) {
  138. sprintf(s, " set group duty to : %d", data[0]);
  139. doubleS.writeSerial(s);
  140. }
  141. for (uint8_t i = 0; i < cnt; i++) {
  142. if (pwm_driver[i].isAvailable()) {
  143. if (pwm_driver[i].set_group_dimming(data[0])) {
  144. if (this->debug)
  145. doubleS.writeSerial(" set!");
  146. } else {
  147. if (this->debug)
  148. doubleS.writeSerial(" error while setting!");
  149. return RETURN_ERROR;
  150. }
  151. }
  152. }
  153. break;
  154. }
  155. // enable/disable the group blinking for a given channel
  156. case 'G': {
  157. uint8_t data[2] = {0}; //parameter 0: channel, parameter 1: bool
  158. returncode = readParameters(cmd_buf_pntr, cmd_len, data, 2);
  159. if (returncode != RETURN_CR) {
  160. return returncode;
  161. }
  162. if (data[0] >= 120) {
  163. return RETURN_ERROR;
  164. }
  165. uint8_t dev = data[0] / 10;
  166. uint8_t port = data[0] % 10;
  167. if (data[1] > 1) {
  168. data[1] = 1;
  169. }
  170. char s[64] = {'\0'};
  171. if (this->debug) {
  172. sprintf(s, " set group blinking of channel %d (dev: %d port: %d) to %d", data[0], dev, port, data[1]);
  173. doubleS.writeSerial(s);
  174. }
  175. if (pwm_driver[dev].isAvailable()) {
  176. if (pwm_driver[dev].set_led_mode(port, data[1]+2)) {
  177. if (this->debug)
  178. doubleS.writeSerial(" set!");
  179. } else {
  180. if (this->debug)
  181. doubleS.writeSerial(" error while setting!");
  182. return RETURN_ERROR;
  183. }
  184. } else {
  185. if (this->debug)
  186. doubleS.writeSerial(" dev not available!");
  187. return RETURN_ERROR;
  188. }
  189. break;
  190. }
  191. // set pwm value to multiple pwm channels
  192. case 'm': {
  193. uint8_t data[CMD_M_MAX_LENGTH] = {0}; //parameter n-1: channels, parameter last: pwm
  194. uint8_t length = CMD_M_MAX_LENGTH;
  195. char tmp[50];
  196. returncode = readParametersWithLength(cmd_buf_pntr, cmd_len, data, &length, MODE_SINGLE_VALUE);
  197. if (returncode != RETURN_CR) {
  198. return returncode;
  199. }
  200. if (this->debug) {
  201. doubleS.writeSerial(" Data: ");
  202. PrintHex8(tmp, data, length);
  203. doubleS.writeSerial(tmp);
  204. sprintf(tmp, " array length: %d", length);
  205. doubleS.writeSerial(tmp);
  206. }
  207. // sort all channels
  208. boolean change_pwm[12*10] = {false};
  209. uint8_t new_pwm[12*10] = {0};
  210. for (uint8_t i = 0; i < data[0]; i++) {
  211. uint8_t channel = data[i+1];
  212. if (!pwm_driver[channel/10].isAvailable()) {
  213. return RETURN_ERROR;
  214. }
  215. new_pwm[channel] = data[length-1];
  216. change_pwm[channel] = true;
  217. }
  218. for (uint8_t i = 0; i < cnt; i++) {
  219. int8_t channel_start = -1;
  220. int8_t channel_end = -1;
  221. //find first and last change
  222. for (uint8_t j = 0; j < 10; j++) {
  223. if (change_pwm[i*10+j]) {
  224. if (channel_start == -1)
  225. channel_start = j;
  226. channel_end = j;
  227. }
  228. }
  229. // if no change for current device jump to next device
  230. if (channel_start == -1 && channel_end == -1) {
  231. continue;
  232. }
  233. if (this->debug) {
  234. sprintf(tmp, "\rdev: %d changes: ", i);
  235. doubleS.writeSerial(tmp);
  236. PrintHex8(tmp, &change_pwm[i*10], 10);
  237. doubleS.writeSerial(tmp);
  238. sprintf(tmp, " start: %d end %d", channel_start, channel_end);
  239. doubleS.writeSerial(tmp);
  240. doubleS.writeSerial("\r values: ");
  241. PrintHex8(tmp, &new_pwm[i*10], 10);
  242. doubleS.writeSerial(tmp);
  243. }
  244. // read current values from devices (drivers internal copy)
  245. for (uint8_t j = channel_start; j <= channel_end; j++) {
  246. if (!change_pwm[i*10+j]) {
  247. new_pwm[i*10+j] = pwm_driver[i].getPWMValue(j);
  248. }
  249. }
  250. if (this->debug) {
  251. doubleS.writeSerial("\rfilled values: ");
  252. PrintHex8(tmp, &new_pwm[i*10], 10);
  253. doubleS.writeSerial(tmp);
  254. }
  255. //write block to current device
  256. boolean ret = pwm_driver[i].set_all_led_pwm(channel_start, channel_end-channel_start+1, &new_pwm[i*10+channel_start]);
  257. if (!ret) {
  258. return RETURN_ERROR;
  259. }
  260. }
  261. break;
  262. }
  263. // set pwm values to pwm channels
  264. case 'n': {
  265. uint8_t data[CMD_N_MAX_LENGTH] = {0}; //parameters: n channels, n pwm values
  266. uint8_t length = CMD_N_MAX_LENGTH;
  267. char tmp[50];
  268. returncode = readParametersWithLength(cmd_buf_pntr, cmd_len, data, &length, MODE_MULTIPLE_VALUE);
  269. if (returncode != RETURN_CR) {
  270. return returncode;
  271. }
  272. if (this->debug) {
  273. doubleS.writeSerial(" Data: ");
  274. PrintHex8(tmp, data, length);
  275. doubleS.writeSerial(tmp);
  276. sprintf(tmp, " array length: %d", length);
  277. doubleS.writeSerial(tmp);
  278. }
  279. // sort all channels
  280. boolean change_pwm[12*10] = {false};
  281. uint8_t new_pwm[12*10] = {0};
  282. for (uint8_t i = 0; i < data[0]; i++) {
  283. uint8_t channel = data[i+1];
  284. if (!pwm_driver[channel/10].isAvailable()) {
  285. return RETURN_ERROR;
  286. }
  287. new_pwm[channel] = data[i+1+data[0]];
  288. change_pwm[channel] = true;
  289. }
  290. for (uint8_t i = 0; i < cnt; i++) {
  291. int8_t channel_start = -1;
  292. int8_t channel_end = -1;
  293. //find first and last change
  294. for (uint8_t j = 0; j < 10; j++) {
  295. if (change_pwm[i*10+j]) {
  296. if (channel_start == -1)
  297. channel_start = j;
  298. channel_end = j;
  299. }
  300. }
  301. // if no change for current device jump to next device
  302. if (channel_start == -1 && channel_end == -1) {
  303. continue;
  304. }
  305. if (this->debug) {
  306. sprintf(tmp, "\rdev: %d changes: ", i);
  307. doubleS.writeSerial(tmp);
  308. PrintHex8(tmp, &change_pwm[i*10], 10);
  309. doubleS.writeSerial(tmp);
  310. sprintf(tmp, " start: %d end %d", channel_start, channel_end);
  311. doubleS.writeSerial(tmp);
  312. doubleS.writeSerial("\r values: ");
  313. PrintHex8(tmp, &new_pwm[i*10], 10);
  314. doubleS.writeSerial(tmp);
  315. }
  316. // read current values from devices (drivers internal copy)
  317. for (uint8_t j = channel_start; j <= channel_end; j++) {
  318. if (!change_pwm[i*10+j]) {
  319. new_pwm[i*10+j] = pwm_driver[i].getPWMValue(j);
  320. }
  321. }
  322. if (this->debug) {
  323. doubleS.writeSerial("\rfilled values: ");
  324. PrintHex8(tmp, &new_pwm[i*10], 10);
  325. doubleS.writeSerial(tmp);
  326. }
  327. //write block to current device
  328. boolean ret = pwm_driver[i].set_all_led_pwm(channel_start, channel_end-channel_start+1, &new_pwm[i*10+channel_start]);
  329. if (!ret) {
  330. return RETURN_ERROR;
  331. }
  332. }
  333. break;
  334. }
  335. // set group blinking for multiple channels
  336. case 'g': {
  337. uint8_t data[CMD_N_MAX_LENGTH] = {0}; //parameter: n channels, n boolean group blinking
  338. uint8_t length = CMD_N_MAX_LENGTH;
  339. char tmp[50];
  340. returncode = readParametersWithLength(cmd_buf_pntr, cmd_len, data, &length, MODE_MULTIPLE_VALUE);
  341. if (returncode != RETURN_CR) {
  342. return returncode;
  343. }
  344. if (this->debug) {
  345. doubleS.writeSerial(" Data: ");
  346. PrintHex8(tmp, data, length);
  347. doubleS.writeSerial(tmp);
  348. sprintf(tmp, " array length: %d", length);
  349. doubleS.writeSerial(tmp);
  350. }
  351. // sort all channels
  352. uint8_t new_mode[12][10] = {0};
  353. uint8_t new_led[12][10] = {0};
  354. uint8_t change_cnt[12] = {0};
  355. for (uint8_t i = 0; i < data[0]; i++) {
  356. uint8_t channel = data[i+1];
  357. if (!pwm_driver[channel/10].isAvailable()) {
  358. return RETURN_ERROR;
  359. }
  360. new_mode[channel/10][change_cnt[channel/10]] = data[i+1+data[0]] + 2;
  361. new_led[channel/10][change_cnt[channel/10]] = channel%10;
  362. change_cnt[channel/10]++;
  363. }
  364. for (uint8_t i = 0; i < cnt; i++) {
  365. // if no change for current device jump to next device
  366. if (change_cnt[i] == 0) {
  367. continue;
  368. }
  369. if (this->debug) {
  370. sprintf(tmp, "\rdev: %d leds: ", i);
  371. doubleS.writeSerial(tmp);
  372. PrintHex8(tmp, new_led[i], 10);
  373. doubleS.writeSerial(tmp);
  374. sprintf(tmp, " cnt: %d\r values: ", change_cnt[i]);
  375. doubleS.writeSerial(tmp);
  376. PrintHex8(tmp, new_mode[i], 10);
  377. doubleS.writeSerial(tmp);
  378. }
  379. //write block to current device
  380. boolean ret = pwm_driver[i].set_all_led_modes(change_cnt[i], new_led[i], new_mode[i]);
  381. if (!ret) {
  382. return RETURN_ERROR;
  383. }
  384. }
  385. break;
  386. }
  387. // dump all available devices
  388. case 'L': {
  389. returncode = readParameters(cmd_buf_pntr, cmd_len, NULL, 0);
  390. if (returncode != RETURN_CR) {
  391. return returncode;
  392. }
  393. char s[64] = {'\0'};
  394. for (uint8_t i = 0; i < cnt; i++) {
  395. if (pwm_driver[i].isAvailable()) {
  396. sprintf(s, "dev %d available (channel: %d to: %d hex: %.2X to %.2X)\r\n", i, i*10, i*10+9, i*10, i*10+9);
  397. doubleS.writeSerial(s);
  398. }
  399. }
  400. break;
  401. }
  402. // toogle debug messages
  403. case 'D': {
  404. returncode = readParameters(cmd_buf_pntr, cmd_len, NULL, 0);
  405. if (returncode != RETURN_CR) {
  406. return returncode;
  407. }
  408. this->debug = !this->debug;
  409. break;
  410. }
  411. default: {
  412. // end with error on unknown commands
  413. return RETURN_ERROR;
  414. }
  415. }
  416. return RETURN_CR;
  417. }
  418. return_code_t cmdInterpreter::readParameters(uint8_t *cmd_buf_pntr, uint8_t cmd_len, uint8_t parameters[], uint8_t num_parameters) {
  419. if (cmd_len != (2+2*num_parameters)) {
  420. return RETURN_ERROR;
  421. }
  422. for (uint8_t i = 0; i < num_parameters; i++) {
  423. parameters[i] = 0;
  424. parameters[i] = ascii2byte(++cmd_buf_pntr) << 4;
  425. parameters[i] += ascii2byte(++cmd_buf_pntr);
  426. }
  427. return RETURN_CR;
  428. }
  429. /**
  430. * this commands reads from a array and checks if the first hex block is a length that matches the rest of the data
  431. * all parameters from string are converted to byte and stored in paramters array.
  432. * the num_parameter pointer reads the maximal length of the parameters array and contains the written paraeters to parameters after the method.
  433. * m controls the meode this method calculated the amount of expected parameters:
  434. * - MODE_SINGLE_VALUE means there is an length field, length*bytes and a value field
  435. * - MODE_MULTUPLE_VALUE means there is an length field, length*bytes and again length*value fields
  436. */
  437. return_code_t cmdInterpreter::readParametersWithLength(uint8_t *cmd_buf_pntr, uint8_t cmd_len, uint8_t parameters[], uint8_t *num_parameters, cmdInterpreter::interpreteMode_t m) {
  438. //check if length is available
  439. if (cmd_len < (2+2)) {
  440. return RETURN_ERROR;
  441. }
  442. parameters[0] = 0;
  443. parameters[0] = ascii2byte(++cmd_buf_pntr) << 4;
  444. parameters[0] += ascii2byte(++cmd_buf_pntr);
  445. //check if length and command has the same length
  446. uint8_t dataLength = 0;
  447. switch(m) {
  448. case MODE_SINGLE_VALUE:
  449. dataLength = 2*parameters[0] + 2;
  450. break;
  451. case MODE_MULTIPLE_VALUE:
  452. dataLength = 2*parameters[0] + 2*parameters[0];
  453. break;
  454. default:
  455. break;
  456. }
  457. if (cmd_len != (2+2+dataLength) || (dataLength/2+1) > *num_parameters) {
  458. return RETURN_ERROR;
  459. }
  460. //interprete parameters from string
  461. for (uint8_t i = 1; i < dataLength+1; i++) {
  462. parameters[i] = 0;
  463. parameters[i] = ascii2byte(++cmd_buf_pntr) << 4;
  464. parameters[i] += ascii2byte(++cmd_buf_pntr);
  465. }
  466. *num_parameters = dataLength/2+1;
  467. return RETURN_CR;
  468. }
  469. /**
  470. * add to buffer and check if end of command is detected
  471. * will return true if a end of command is detected
  472. */
  473. boolean cmdInterpreter::addToBuffer(uint8_t d) {
  474. *this->cmdBuffer_ptr = d;
  475. if (d == CMD_CR) { //check if the last character is the end character
  476. if (this->debug) {
  477. doubleS.writeSerial("Buffer: ");
  478. char tmp[50];
  479. PrintHex8(tmp, this->cmdBuffer, 20);
  480. doubleS.writeSerial(tmp);
  481. }
  482. return true;
  483. } else { //if not, move pointer forward to next array address
  484. (this->cmdBuffer_ptr)++;
  485. }
  486. return false;
  487. }
  488. /**
  489. * prints 8-bit data in hex with leading zeroes
  490. */
  491. void PrintHex8(char *dstptr, uint8_t *srcdata, uint8_t length) {
  492. for (int i=0; i<length; i++) {
  493. sprintf(dstptr+(i*2), "%.2X",srcdata[i]);
  494. }
  495. }
  496. /**
  497. * prints boolean data
  498. */
  499. void PrintHex8(char *dstptr, boolean *srcdata, uint8_t length) {
  500. for (int i=0; i<length; i++) {
  501. sprintf(dstptr+(i*2), " %.1X",srcdata[i]);
  502. }
  503. }
  504. /**
  505. * takes a pointer to a string and convert the current characters to a binary
  506. */
  507. uint8_t ascii2byte(const uint8_t *val) {
  508. uint8_t temp = *val;
  509. if (temp > 0x60)
  510. temp -= 0x27; // convert chars a-f
  511. else if (temp > 0x40)
  512. temp -= 0x07; // convert chars A-F
  513. temp -= 0x30; // convert chars 0-9
  514. return (uint8_t) (temp & 0x0F);
  515. }