Represents a single device. Take measurements, send commands. Needs SDI12 bus instance as argument. Allows to detect devices on the bus
SDI12_device.h@23:59447d795751, 2018-08-09 (annotated)
- Committer:
- amateusz
- Date:
- Thu Aug 09 10:44:42 2018 +0000
- Revision:
- 23:59447d795751
- Parent:
- 22:c6e99d486304
idk
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
amateusz | 0:d58ebb2bed9a | 1 | #include "SDI12.h" |
amateusz | 0:d58ebb2bed9a | 2 | #include <string> |
amateusz | 2:e2db05bc4708 | 3 | #include "mbed.h" |
amateusz | 22:c6e99d486304 | 4 | extern void printStackStats(char num); |
amateusz | 0:d58ebb2bed9a | 5 | |
amateusz | 0:d58ebb2bed9a | 6 | class SDI12_device |
amateusz | 0:d58ebb2bed9a | 7 | { |
amateusz | 0:d58ebb2bed9a | 8 | public: |
amateusz | 0:d58ebb2bed9a | 9 | struct Identification_struct { |
amateusz | 0:d58ebb2bed9a | 10 | unsigned char sdi_version; |
amateusz | 0:d58ebb2bed9a | 11 | char company[8+1]; |
amateusz | 0:d58ebb2bed9a | 12 | char model[6+1]; |
amateusz | 0:d58ebb2bed9a | 13 | char version[3+1]; |
amateusz | 0:d58ebb2bed9a | 14 | }; |
amateusz | 0:d58ebb2bed9a | 15 | |
amateusz | 22:c6e99d486304 | 16 | Identification_struct _ident_struct; |
amateusz | 22:c6e99d486304 | 17 | |
amateusz | 0:d58ebb2bed9a | 18 | struct Measurement_struct { |
amateusz | 0:d58ebb2bed9a | 19 | unsigned long timestampMeasurementReadyAt; |
amateusz | 4:11438803516c | 20 | char readyIn; |
amateusz | 0:d58ebb2bed9a | 21 | bool _measurementAlreadyRead; |
amateusz | 0:d58ebb2bed9a | 22 | char count; |
amateusz | 22:c6e99d486304 | 23 | // float * values; // not float values [] !! :O |
amateusz | 22:c6e99d486304 | 24 | float values[20]; // !! :O |
amateusz | 0:d58ebb2bed9a | 25 | }; |
amateusz | 0:d58ebb2bed9a | 26 | |
amateusz | 19:a29b98ae0005 | 27 | const static int detect(SDI12 &sdi12, char indices[], int stop = -1, int start = -1) { |
amateusz | 0:d58ebb2bed9a | 28 | char found = 0; |
amateusz | 10:efd1e313fe8c | 29 | for (size_t i = (start==-1?0:start); i < (stop==-1?ADDRRICES.size():stop+1); ++i) { |
amateusz | 21:aa7b255c5896 | 30 | const std::string question = std::string(1, ADDRRICES[i]) + "!"; |
amateusz | 0:d58ebb2bed9a | 31 | sdi12.sendCommand(question); |
amateusz | 0:d58ebb2bed9a | 32 | Timer detectTimer; |
amateusz | 0:d58ebb2bed9a | 33 | detectTimer.start(); |
amateusz | 15:530bf2e09814 | 34 | while (detectTimer.read_ms() < RESPONSE_TIMEOUT * 4) { |
amateusz | 5:e53104570fde | 35 | if (sdi12.RxInProgress()) detectTimer.reset(); |
amateusz | 7:1f506c65c4e8 | 36 | if (sdi12.RxBufferAvailable()) { |
amateusz | 7:1f506c65c4e8 | 37 | unsigned char bufferSize = sdi12.RxBufferAvailable(); |
amateusz | 7:1f506c65c4e8 | 38 | char buffer[bufferSize]; |
amateusz | 7:1f506c65c4e8 | 39 | sdi12.getRxBuffer(buffer); |
amateusz | 7:1f506c65c4e8 | 40 | // if first char is valid address char |
amateusz | 7:1f506c65c4e8 | 41 | if (ADDRRICES.find(buffer[0])) { |
amateusz | 7:1f506c65c4e8 | 42 | indices[found++] = buffer[0]; |
amateusz | 19:a29b98ae0005 | 43 | debug("FOUND (%d): %d -> %c\r\n", found, i, indices[found-1]); |
amateusz | 7:1f506c65c4e8 | 44 | } |
amateusz | 7:1f506c65c4e8 | 45 | } |
amateusz | 0:d58ebb2bed9a | 46 | } |
amateusz | 13:0093792f2325 | 47 | osDelay(100); |
amateusz | 0:d58ebb2bed9a | 48 | } |
amateusz | 0:d58ebb2bed9a | 49 | return found; |
amateusz | 0:d58ebb2bed9a | 50 | }; |
amateusz | 17:5cabfc047b40 | 51 | |
amateusz | 17:5cabfc047b40 | 52 | SDI12_device() { |
amateusz | 22:c6e99d486304 | 53 | debug("default in %s\r\n", __FILE__); |
amateusz | 17:5cabfc047b40 | 54 | } |
amateusz | 17:5cabfc047b40 | 55 | |
amateusz | 18:365e8aa4002c | 56 | SDI12_device (SDI12 *inst, char address) : _sdi12(inst) { |
amateusz | 15:530bf2e09814 | 57 | setAddress(address); |
amateusz | 1:6b1a21925a81 | 58 | if(getIdentification(_ident_struct)); |
amateusz | 0:d58ebb2bed9a | 59 | }; |
amateusz | 15:530bf2e09814 | 60 | |
amateusz | 22:c6e99d486304 | 61 | SDI12_device (const SDI12_device &old) { |
amateusz | 22:c6e99d486304 | 62 | debug("copy in %s\r\n", __FILE__); |
amateusz | 15:530bf2e09814 | 63 | _address = old._address; |
amateusz | 15:530bf2e09814 | 64 | _ident_struct = old._ident_struct; |
amateusz | 22:c6e99d486304 | 65 | _sdi12 = old._sdi12; |
amateusz | 15:530bf2e09814 | 66 | _measurement = old._measurement; |
amateusz | 22:c6e99d486304 | 67 | _measurementReady = false; |
amateusz | 15:530bf2e09814 | 68 | _valuesReadyCount = old._valuesReadyCount; |
amateusz | 22:c6e99d486304 | 69 | _measurementReady = old._measurementReady; // why?!?!! new _measureStruct here worked ?! |
amateusz | 22:c6e99d486304 | 70 | } |
amateusz | 22:c6e99d486304 | 71 | |
amateusz | 22:c6e99d486304 | 72 | SDI12_device & operator= (const SDI12_device & old) { |
amateusz | 22:c6e99d486304 | 73 | debug("assign in %s\r\n", __FILE__); |
amateusz | 22:c6e99d486304 | 74 | _address = old._address; |
amateusz | 22:c6e99d486304 | 75 | _ident_struct = old._ident_struct; |
amateusz | 22:c6e99d486304 | 76 | _sdi12 = old._sdi12; |
amateusz | 22:c6e99d486304 | 77 | _measurement = old._measurement; |
amateusz | 22:c6e99d486304 | 78 | _measurementReady = false; |
amateusz | 22:c6e99d486304 | 79 | _valuesReadyCount = old._valuesReadyCount; |
amateusz | 22:c6e99d486304 | 80 | _measurementReady = old._measurementReady; // why?!?!! new _measureStruct here worked ?! |
amateusz | 22:c6e99d486304 | 81 | |
amateusz | 19:a29b98ae0005 | 82 | return *this; |
amateusz | 15:530bf2e09814 | 83 | } |
amateusz | 15:530bf2e09814 | 84 | |
amateusz | 10:efd1e313fe8c | 85 | ~SDI12_device () { |
amateusz | 22:c6e99d486304 | 86 | debug("destructor in %s\r\n", __FILE__); |
amateusz | 10:efd1e313fe8c | 87 | } |
amateusz | 22:c6e99d486304 | 88 | |
amateusz | 19:a29b98ae0005 | 89 | bool present() { |
amateusz | 21:aa7b255c5896 | 90 | const std::string question = std::string(1, _address) + std::string("!"); |
amateusz | 19:a29b98ae0005 | 91 | _sdi12->sendCommand(question); |
amateusz | 19:a29b98ae0005 | 92 | |
amateusz | 19:a29b98ae0005 | 93 | Timer timeout; |
amateusz | 19:a29b98ae0005 | 94 | timeout.start(); |
amateusz | 19:a29b98ae0005 | 95 | while (sdi12.RxBufferAvailable() == 0) { |
amateusz | 19:a29b98ae0005 | 96 | if (sdi12.RxInProgress()) timeout.reset(); |
amateusz | 19:a29b98ae0005 | 97 | if(timeout.read_ms() > RESPONSE_TIMEOUT) { |
amateusz | 19:a29b98ae0005 | 98 | return false; |
amateusz | 19:a29b98ae0005 | 99 | } |
amateusz | 19:a29b98ae0005 | 100 | } |
amateusz | 19:a29b98ae0005 | 101 | unsigned char bufferSize = sdi12.RxBufferAvailable(); |
amateusz | 19:a29b98ae0005 | 102 | char buffer[bufferSize]; |
amateusz | 19:a29b98ae0005 | 103 | sdi12.getRxBuffer(buffer); // only to flush the buffer |
amateusz | 19:a29b98ae0005 | 104 | return true; |
amateusz | 19:a29b98ae0005 | 105 | } |
amateusz | 15:530bf2e09814 | 106 | // concurrent means that device under measurement won't signal its prempt readiness to the bus |
amateusz | 13:0093792f2325 | 107 | int measure(bool concurrent = true, char group = '0') { |
amateusz | 4:11438803516c | 108 | |
amateusz | 21:aa7b255c5896 | 109 | const std::string question = std::string(1, _address) + std::string(concurrent?"C": (group=='0'?"M":("M"+std::string(1,group)))) + std::string("!"); |
amateusz | 21:aa7b255c5896 | 110 | // if (group != '0') // ABOVE DOES THIS |
amateusz | 20:abccaaa16955 | 111 | // question.insert(2, 1, group); // e.g. 2M! -> 2M1! |
amateusz | 15:530bf2e09814 | 112 | _sdi12->sendCommand(question); |
amateusz | 2:e2db05bc4708 | 113 | |
amateusz | 2:e2db05bc4708 | 114 | Timer timeout; |
amateusz | 2:e2db05bc4708 | 115 | timeout.start(); |
amateusz | 2:e2db05bc4708 | 116 | while (sdi12.RxBufferAvailable() == 0) { |
amateusz | 2:e2db05bc4708 | 117 | if (sdi12.RxInProgress()) timeout.reset(); |
amateusz | 5:e53104570fde | 118 | if(timeout.read_ms() > RESPONSE_TIMEOUT) { |
amateusz | 19:a29b98ae0005 | 119 | return -2; |
amateusz | 2:e2db05bc4708 | 120 | } |
amateusz | 2:e2db05bc4708 | 121 | } |
amateusz | 10:efd1e313fe8c | 122 | |
amateusz | 2:e2db05bc4708 | 123 | unsigned char bufferSize = sdi12.RxBufferAvailable(); |
amateusz | 2:e2db05bc4708 | 124 | char buffer[bufferSize+1]; |
amateusz | 2:e2db05bc4708 | 125 | sdi12.getRxBuffer(buffer); |
amateusz | 2:e2db05bc4708 | 126 | buffer[bufferSize] = '\0'; |
amateusz | 2:e2db05bc4708 | 127 | std::string response(buffer); |
amateusz | 2:e2db05bc4708 | 128 | |
amateusz | 2:e2db05bc4708 | 129 | if (response[0] == _address) { |
amateusz | 2:e2db05bc4708 | 130 | char measurementsCount = std::atoi(response.substr(1+3,(concurrent?2:1)).c_str()); |
amateusz | 2:e2db05bc4708 | 131 | char waitFor = std::atoi(response.substr(1,3).c_str()); |
amateusz | 2:e2db05bc4708 | 132 | _measurement.count = measurementsCount; |
amateusz | 22:c6e99d486304 | 133 | |
amateusz | 4:11438803516c | 134 | _measurement.readyIn = waitFor; |
amateusz | 2:e2db05bc4708 | 135 | // _measurement.timestampMeasurementReadyAt |
amateusz | 2:e2db05bc4708 | 136 | debug("wait for %d measurement for %d seconds...", _measurement.count, waitFor); |
amateusz | 11:e0ccc1072c42 | 137 | _valuesReadyCount = 0; |
amateusz | 13:0093792f2325 | 138 | _measurementReady = false; |
amateusz | 2:e2db05bc4708 | 139 | return 0; |
amateusz | 2:e2db05bc4708 | 140 | } |
amateusz | 2:e2db05bc4708 | 141 | return -1; |
amateusz | 2:e2db05bc4708 | 142 | }; |
amateusz | 13:0093792f2325 | 143 | |
amateusz | 4:11438803516c | 144 | char readyIn() { |
amateusz | 4:11438803516c | 145 | return _measurement.readyIn; |
amateusz | 4:11438803516c | 146 | } |
amateusz | 4:11438803516c | 147 | |
amateusz | 13:0093792f2325 | 148 | bool isReady() { |
amateusz | 13:0093792f2325 | 149 | return _measurementReady; |
amateusz | 13:0093792f2325 | 150 | } |
amateusz | 13:0093792f2325 | 151 | |
amateusz | 11:e0ccc1072c42 | 152 | bool read(char group = '0') { |
amateusz | 22:c6e99d486304 | 153 | char group_a[] = {group}; |
amateusz | 22:c6e99d486304 | 154 | printStackStats(std::atoi(group_a)); |
amateusz | 11:e0ccc1072c42 | 155 | // Measurement (M), Continuous (R), and Concurrent (C) commands and subsequent Data (D) |
amateusz | 21:aa7b255c5896 | 156 | const std::string question = std::string(1, _address) + std::string("D") + std::string(1, group)+ std::string("!"); |
amateusz | 15:530bf2e09814 | 157 | _sdi12->sendCommand(question); |
amateusz | 2:e2db05bc4708 | 158 | |
amateusz | 2:e2db05bc4708 | 159 | Timer timeout; |
amateusz | 2:e2db05bc4708 | 160 | timeout.start(); |
amateusz | 2:e2db05bc4708 | 161 | while (sdi12.RxBufferAvailable() == 0) { |
amateusz | 2:e2db05bc4708 | 162 | if (sdi12.RxInProgress()) timeout.reset(); |
amateusz | 5:e53104570fde | 163 | if(timeout.read_ms() > RESPONSE_TIMEOUT) { |
amateusz | 2:e2db05bc4708 | 164 | return false; |
amateusz | 2:e2db05bc4708 | 165 | } |
amateusz | 2:e2db05bc4708 | 166 | } |
amateusz | 2:e2db05bc4708 | 167 | unsigned char bufferSize = sdi12.RxBufferAvailable(); |
amateusz | 2:e2db05bc4708 | 168 | char buffer[bufferSize+1]; |
amateusz | 2:e2db05bc4708 | 169 | sdi12.getRxBuffer(buffer); |
amateusz | 2:e2db05bc4708 | 170 | buffer[bufferSize] = '\0'; |
amateusz | 17:5cabfc047b40 | 171 | const std::string response(buffer); |
amateusz | 2:e2db05bc4708 | 172 | |
amateusz | 23:59447d795751 | 173 | // debug("parser recv: %s\r\n", response); |
amateusz | 23:59447d795751 | 174 | if (response[0] == _address && response.size() > 1) { |
amateusz | 22:c6e99d486304 | 175 | // debug("__%s__\r\n", response); |
amateusz | 3:2847f7c543d3 | 176 | // parser here |
amateusz | 17:5cabfc047b40 | 177 | // const!!! response = response.substr(1); // to limit repeting this operation later. i.e. extract only values e.g. +21.3-123+123 |
amateusz | 3:2847f7c543d3 | 178 | // the only two possible delimeters of a value |
amateusz | 17:5cabfc047b40 | 179 | size_t next_start_index = response.find_first_of("-+", 0 + 1); // address offset! |
amateusz | 3:2847f7c543d3 | 180 | size_t next_end_index; |
amateusz | 3:2847f7c543d3 | 181 | // ready, steady, parse!! |
amateusz | 3:2847f7c543d3 | 182 | while(next_start_index != std::string::npos) { |
amateusz | 3:2847f7c543d3 | 183 | // determine start index: |
amateusz | 3:2847f7c543d3 | 184 | next_end_index = response.find_first_of("-+", next_start_index + 1); // std::substr is prepared to take std::npos |
amateusz | 17:5cabfc047b40 | 185 | |
amateusz | 3:2847f7c543d3 | 186 | float value = std::atof(response.substr(next_start_index, next_end_index).c_str()); |
amateusz | 4:11438803516c | 187 | // debug("parsed: %f\r\n", value); |
amateusz | 11:e0ccc1072c42 | 188 | _measurement.values[_valuesReadyCount++] = value; |
amateusz | 3:2847f7c543d3 | 189 | next_start_index = response.find_first_of("-+", next_end_index); |
amateusz | 3:2847f7c543d3 | 190 | } |
amateusz | 11:e0ccc1072c42 | 191 | // if current parsing doesn't return all the expexted measurements, then press harder and poll further Dx commands. RECURSION HERE *.* |
amateusz | 22:c6e99d486304 | 192 | if (_valuesReadyCount < _measurement.count) { |
amateusz | 22:c6e99d486304 | 193 | debug("values count: %d\r\n", _valuesReadyCount); |
amateusz | 11:e0ccc1072c42 | 194 | read(group + 1); |
amateusz | 22:c6e99d486304 | 195 | } else { |
amateusz | 13:0093792f2325 | 196 | _measurementReady = true; |
amateusz | 13:0093792f2325 | 197 | } |
amateusz | 3:2847f7c543d3 | 198 | } |
amateusz | 4:11438803516c | 199 | return true; |
amateusz | 2:e2db05bc4708 | 200 | } |
amateusz | 4:11438803516c | 201 | |
amateusz | 4:11438803516c | 202 | |
amateusz | 0:d58ebb2bed9a | 203 | // |
amateusz | 4:11438803516c | 204 | const unsigned char getMeasurementCount() { |
amateusz | 2:e2db05bc4708 | 205 | return _measurement.count; |
amateusz | 2:e2db05bc4708 | 206 | } |
amateusz | 2:e2db05bc4708 | 207 | |
amateusz | 4:11438803516c | 208 | const float getMeasurementValue(char which) { |
amateusz | 13:0093792f2325 | 209 | _measurementReady = false; // reading /any/ of the values resets this |
amateusz | 2:e2db05bc4708 | 210 | return _measurement.values[which]; |
amateusz | 2:e2db05bc4708 | 211 | } |
amateusz | 2:e2db05bc4708 | 212 | |
amateusz | 0:d58ebb2bed9a | 213 | void setAddress(char address) { |
amateusz | 0:d58ebb2bed9a | 214 | _address = address; |
amateusz | 0:d58ebb2bed9a | 215 | }; |
amateusz | 0:d58ebb2bed9a | 216 | // |
amateusz | 0:d58ebb2bed9a | 217 | // |
amateusz | 0:d58ebb2bed9a | 218 | private: |
amateusz | 13:0093792f2325 | 219 | static const int RESPONSE_TIMEOUT = 15+1; // device should start to respond within [ms] |
amateusz | 5:e53104570fde | 220 | static const std::string ADDRRICES; // as in index -> indices, |
amateusz | 5:e53104570fde | 221 | |
amateusz | 15:530bf2e09814 | 222 | SDI12 *_sdi12; |
amateusz | 0:d58ebb2bed9a | 223 | char _address; |
amateusz | 17:5cabfc047b40 | 224 | |
amateusz | 0:d58ebb2bed9a | 225 | Measurement_struct _measurement; |
amateusz | 11:e0ccc1072c42 | 226 | size_t _valuesReadyCount; |
amateusz | 13:0093792f2325 | 227 | bool _measurementReady; |
amateusz | 0:d58ebb2bed9a | 228 | |
amateusz | 1:6b1a21925a81 | 229 | bool getIdentification(Identification_struct &ident) { |
amateusz | 15:530bf2e09814 | 230 | // _sdi12->sendCommand(std::string(_address) + "I!"); |
amateusz | 21:aa7b255c5896 | 231 | const std::string question = std::string(1, _address) + "I!"; |
amateusz | 15:530bf2e09814 | 232 | _sdi12->sendCommand(question); |
amateusz | 0:d58ebb2bed9a | 233 | |
amateusz | 1:6b1a21925a81 | 234 | Timer timeout; |
amateusz | 1:6b1a21925a81 | 235 | timeout.start(); |
amateusz | 1:6b1a21925a81 | 236 | while (sdi12.RxBufferAvailable() == 0) { |
amateusz | 1:6b1a21925a81 | 237 | if (sdi12.RxInProgress()) timeout.reset(); |
amateusz | 5:e53104570fde | 238 | if(timeout.read_ms() > RESPONSE_TIMEOUT) { |
amateusz | 1:6b1a21925a81 | 239 | return false; |
amateusz | 1:6b1a21925a81 | 240 | } |
amateusz | 1:6b1a21925a81 | 241 | } |
amateusz | 1:6b1a21925a81 | 242 | unsigned char bufferSize = sdi12.RxBufferAvailable(); |
amateusz | 1:6b1a21925a81 | 243 | char buffer[bufferSize+1]; |
amateusz | 1:6b1a21925a81 | 244 | sdi12.getRxBuffer(buffer); |
amateusz | 1:6b1a21925a81 | 245 | buffer[bufferSize] = '\0'; |
amateusz | 17:5cabfc047b40 | 246 | const std::string response(buffer); |
amateusz | 0:d58ebb2bed9a | 247 | |
amateusz | 1:6b1a21925a81 | 248 | if (response[0] == _address) { |
amateusz | 1:6b1a21925a81 | 249 | // e.g.: 113DECAGON GS3 402 |
amateusz | 2:e2db05bc4708 | 250 | |
amateusz | 1:6b1a21925a81 | 251 | ident.sdi_version = std::atoi(response.substr(1, 2).c_str()); |
amateusz | 17:5cabfc047b40 | 252 | const std::string tempStr = response.substr(3, 8); |
amateusz | 1:6b1a21925a81 | 253 | strcpy(ident.company, tempStr.c_str()); |
amateusz | 1:6b1a21925a81 | 254 | |
amateusz | 17:5cabfc047b40 | 255 | strcpy(ident.model, response.substr(3+8, 6).c_str()); |
amateusz | 1:6b1a21925a81 | 256 | |
amateusz | 17:5cabfc047b40 | 257 | strcpy(ident.version, response.substr(3+8+6, 3).c_str()); |
amateusz | 1:6b1a21925a81 | 258 | return true; |
amateusz | 0:d58ebb2bed9a | 259 | } |
amateusz | 1:6b1a21925a81 | 260 | return false; |
amateusz | 1:6b1a21925a81 | 261 | } |
amateusz | 5:e53104570fde | 262 | }; |
amateusz | 5:e53104570fde | 263 | |
amateusz | 5:e53104570fde | 264 | const std::string SDI12_device::ADDRRICES = "0123456789abcdefgijklmnoprstuvwxyzABCDEFGHIJKLMNOPRSTUVWXYZ"; |