/* Sense/Net Bus Command-Line Utility. * Copyright 2015 OSU CGRG, Jaiswal Lab, and Tyler Lab. All rights reserved. * Written by Nick Ames . */ #include "sense-net.h" #include "soil-sensor.h" #include #include #include #include #include const char *Usage = "sn-util performs low-level operations on a Sense/Net bus.\n" "Usage:\n" " sn-util get \n" " Get data from a node. If successful, two lines of CSV-formatted data will\n" " be sent to stdout: a column header and a row of data.\n" " Node is the address of the node from which to retrieve data.\n" " sn-util poll\n" " Dicover nodes on the bus, then poll them for data.\n" " A CSV-formatted column header will be printed\n" " to stdout, followed by CSV-formatted lines of data.\n" " sn-util send [data]\n" " Send a packet to a node.\n" " Serial port is the path to the serial port to use.\n" " Command is an integer from 0-7, inclusive.\n" " Node is the destination address of the packet, 9 bytes, expressed in hexadecimal.\n" " Data is the data to send in the packet, 0-128 bytes (inclusive), expressed in hexadecimal.\n" " Data may be omitted.\n" " The reply, if any, will be printed to stdout as a CSV line:\n" " Code (REPLY | NO_REPLY | BUS_ERROR), Node, Status Code, Reply Data\n" " On no reply or bus error status code and reply data are empty.\n" " sn-util enum\n" " Enumerate all nodes on the Sense/Net bus. Output is a CSV file to stdout:\n" " Node, Typecode\n" " Serial port is the path to the serial port to use.\n" " sn-util --help\n" " Print this message.\n" " sn-util --version\n" " Print version information.\n"; const char *Version = "sn-util version 1.\n" "Copyright 2015 OSU CGRB, Tyler Lab, and Jaiswal Lab.\n" "Written by Nick Ames .\n"; /* Convert a hexadecimal string into a memory block. * ptr is set to point to a new block of memory containing the constant. * It should be free'd when no longer needed. On error, ptr is set to NULL. * On success, the size of the block is returned. On error, -1 is returned. * Specific error messages are printed to stderr. */ int mem_hex(char **ptr, const char *str){ if(ptr == NULL){ return -1; } if(str == NULL){ *ptr = NULL; return 0; } /* Check for invalid characters. Valid characters are * 0-9, A-F, X (letters lower or upper case), comma, space, and tab. */ int l; for(l = 0; str[l] != '\0'; l++){ if(!isxdigit(str[l]) && tolower(str[l]) != 'x' && str[l] != ' ' && str[l] != ',' && str[l] != '\t'){ fprintf(stderr, "sn-util: Invalid character %c in hexadecimal constant %s.\n", str[l], str); return -1; } } /* At this point, l is set to the number of characters in the string. */ *ptr = calloc(l/2 + 1, 1); if(NULL == *ptr){ fprintf(stderr, "sn-util: Malloc failure."); return -1; } /* Load data into the memory block. */ int si,mi; for(si = 0, mi=0; si < l; si++){ switch(str[si]){ case ',': /* Fall-through */ case ' ': /* Fall-through */ case '\n': /* Fall-through */ case '\t': break; case 'x': /* Fall-through */ case 'X': /* When interpreting an x-prefixed block, an implied 0 nybble must * be inserted when there are an odd number of characters in the block. */ /* Empty statement to avoid C technicalities: */; int b, bi; for(bi=(si+1),b=0; bi < l && isxdigit(str[bi]); bi++,b++){ /* Count the number of characters in this 0x-prefixed block. */ } if(b % 2)mi++; break; default: if('0' == str[si]){ if(si != l-1 && 'x' == tolower(str[si+1]))continue; } char tempstr[2]; tempstr[1] = '\0'; if(mi % 2){ tempstr[0] = str[si]; (*ptr)[mi/2] |= strtol(tempstr, NULL, 16); mi++; } else { tempstr[0] = str[si]; (*ptr)[mi/2] |= strtol(tempstr, NULL, 16) << 4; mi++; } } } if(mi % 2){ fprintf(stderr, "sn-util: Odd number of nybbles in hexadecimal constant %s\n", str); free(*ptr); *ptr = NULL; return -1; } else { return mi/2; } } /* Print a block of memory to stdout as a hexadecimal constant, prefixed with "0x". */ void print_hex(void *data, int size){ if(0 == size)return; uint8_t *udata = (uint8_t *) data; printf("0x"); for(int i=0;i 7){ fputs("sn-util: Error: Expected a command value between 0 and 7 (inclusive)\n", stderr); return -1; } node_size = mem_hex(&node, argv[4]); if(node_size != 9){ fputs("sn-util: Error: Expected nine bytes for node address\n", stderr); return -1; } if(6 == argc){ data_size = mem_hex(&data, argv[5]); if(data_size < 0 || data_size > 128){ fputs("sn-util: Error: Expected between 0 and 128 bytes (inclusive) of packet data\n", stderr); return -1; } } else { /* No data argument supplied. */ data_size = 0; data = NULL; } if(sn_init(argv[1])){ fputs("sn-util: Error: Could not open serial port", stderr); return -1; } r = sn_send_packet(command, (uint8_t *) node, data, data_size, &status, reply_data, &reply_size); if(0 == r){ printf("NO_REPLY,"); print_hex(node, 9); printf(",,\n"); } else if(1 == r){ printf("REPLY,"); print_hex(node, 9); printf(",%d,", status); print_hex(reply_data, reply_size); printf("\n"); } else { printf("BUS_ERROR,"); print_hex(node, 9); printf(",,\n"); } sn_shutdown(); free(node); if(NULL != data)free(data); } else if(0 == strcmp(argv[2], "enum")){ if(argc != 3){ fputs(Usage, stderr); return -1; } if(sn_init(argv[1])){ fputs("sn-util: Error: Could not open serial port", stderr); return -1; } node_enum_t *list; int num_nodes; sn_enumerate(&list, &num_nodes); if(num_nodes == 0){ return 0; } else { printf("Node_ID, Typecode, Type_Description\n"); } for(int i=0;i < num_nodes;i++){ print_hex(&(list[i].id), 9); printf(", 0x%04X, %s\n", list[i].type, sn_type_str(list[i].type)); } free(list); return 0; } else if(0 == strcmp(argv[2], "get")){ if(argc != 4){ fputs(Usage, stderr); return -1; } if(sn_init(argv[1])){ fputs("sn-util: Error: Could not open serial port", stderr); return -1; } uint8_t *node; int node_size; node_size = mem_hex((char **) &node, argv[3]); if(node_size != 9){ fputs("sn-util: Error: Expected nine bytes for node address\n", stderr); return -1; } char buf[128]; int buf_size; soil_print_header(stdout); for(int i=0;i<3;i++){ /* Retry several times if we get an error. */ if(sn_get_data(node, buf, &buf_size))continue; if(0 == soil_parse_data(stdout, node, (uint8_t *) buf, buf_size))break; } return 0; } else if(0 == strcmp(argv[2], "poll")){ if(argc != 3){ fputs(Usage, stderr); return -1; } if(sn_init(argv[1])){ fputs("sn-util: Error: Could not open serial port", stderr); return -1; } node_enum_t *list; int num_nodes; fprintf(stdout, "Enumerating nodes... "); sn_enumerate(&list, &num_nodes); if(num_nodes == 0){ fprintf(stdout, "no nodes found.\n"); return 0; } else { fprintf(stdout, "%d nodes found.\n", num_nodes); } //TODO: Make this work for other sensor types. soil_print_header(stdout); while(1){ for(int n=0; n < num_nodes; n++){ char buf[128]; int buf_size; for(int i=0;i<3;i++){ /* Retry several times if we get an error. */ if(sn_get_data(list[n].id, buf, &buf_size))continue; if(0 == soil_parse_data(stdout, list[n].id, (uint8_t *) buf, buf_size))break; } } sleep(1); } free(list); return 0; } else { fputs(Usage, stderr); return -1; } } return 0; }