// vim: noai:sw=2:tw=88 #include "NoiseSensor.h" #include "time.h" #include #include ESP32Time t; bool NoiseSensorClass::begin() { _PACKET_SIZE = ( _tick_count + LORA_HEADER_LENGTH + BATTERY_FIELD_SIZE ); _REQUEST_SIZE = ( PACKET_COUNT * _PACKET_SIZE * 2 ); // * 2, it's 16 bit. for (int i = 0; i < PACKET_COUNT; i++) { _nodes[i] = new NoiseSensorNode(i, _PACKET_SIZE * 2); } if (this->_tick_duration < 2) { Serial.println("--> Using a tick duration of less than two seconds. This " "will fail unless you've increased the MCUs SYSCLK!"); } if (this->_tick_count >= 100) { Serial.println( "--> Using a tick count of more than 100 gets dangerously close to the " "LoRa transmission limitations and is not supported as of now."); } if (!setupNTPClient()) { Serial.println("!!!-------------------------!!!"); Serial.println("NTPClient couldn't be started."); Serial.println("!!!-------------------------!!!"); return false; } Wire.begin(21, 22, 400000); sendSyncBeacon(); this->_first_sync = 1; } bool NoiseSensorClass::begin(uint8_t tick_count) { this->_tick_count = tick_count; this->begin(); } bool NoiseSensorClass::begin(uint8_t tick_count, uint8_t tick_duration) { this->_tick_count = tick_count; this->_tick_duration = tick_duration; this->begin(); } bool NoiseSensorClass::begin(uint8_t tick_count, uint8_t tick_duration, uint8_t tx_offset) { this->_tick_count = tick_count; this->_tick_duration = tick_duration; this->_tx_offset = tx_offset; this->begin(); } void NoiseSensorClass::setIds(const char* sensebox_ids[], const char* sensor_ids[]) { for (int i = 0; i < PACKET_COUNT; i++) { char tmp[24] = {0}; _nodes[i]->sensebox_id = sensebox_ids[i]; _nodes[i]->sensor_id = sensor_ids[i]; } } bool NoiseSensorClass::beaconReady() { if (t.getEpoch() - _last_sync_time == (this->_tick_count * this->_tick_duration)) { Serial.println("---->Beacon ready"); return true; } return false; } bool NoiseSensorClass::requestReady() { // 1 second as tolerance if (!this->_first_sync && t.getEpoch() - _last_sync_time == (5 * this->_tx_offset * this->_tick_duration + 1)) { Serial.println("---->Request ready"); return true; } return false; } bool NoiseSensorClass::read() { for (int pkt = 0; pkt < PACKET_COUNT; pkt++) { NoiseSensorNode* node = _nodes[pkt]; // Calculate the time of the sync message that triggered this packet. uint32_t time_offset = (_tick_count * _tick_duration); // TODO Move node->_sync_message_ts = this->_last_sync_time - time_offset; /* Serial.print("Last sync time: "); Serial.println(_last_sync_time); Serial.print("Offset: "); Serial.println(time_offset); Serial.print("TS: "); Serial.println(_nodes[pkt]->_sync_message_ts); Serial.print(minute(_nodes[pkt]->_sync_message_ts)); Serial.print(":"); Serial.println(second(_nodes[pkt]->_sync_message_ts)); */ /* if (node->missing_answers > 0) { node->_sync_message_ts -= time_offset * node->missing_answers; } */ Wire.requestFrom(_i2c_address, _PACKET_SIZE * 2); char req_buf[_PACKET_SIZE * 2] = {0}; uint8_t count = 0; while (Wire.available()) { char c = Wire.read(); req_buf[count] = c; count++; } // TODO There's probably a neater way than doing these low level operations. for (int i = 0; i < _PACKET_SIZE * 2; i+=2) { uint16_t n = req_buf[i+1] << 8; n |= req_buf[i]; node->_measurements[i/2] = n; Serial.print(n); Serial.print(" "); } Serial.println(); } // Skip 0, it has no battery. for (int pkt = 1; pkt < PACKET_COUNT; pkt++) { // TODO Move this to a sane place (own function). if (_nodes[pkt]->_measurements[_PACKET_SIZE - 1]) { Serial.print("Battery low on device"); Serial.println(pkt); } } Serial.println("Reading done."); return true; } void NoiseSensorClass::printMeasurements(uint8_t node_id) { uint32_t ts = this->_nodes[node_id]->_sync_message_ts; char printbuf[32]; sprintf(printbuf, "- ID %u - ", node_id); Serial.println(printbuf); for (int i = 2; i < (this->_PACKET_SIZE - BATTERY_FIELD_SIZE); i++) { // TODO Timestamps Serial.println(this->_nodes[node_id]->_measurements[i]); ts += this->_tick_duration; } } bool NoiseSensorClass::sendSyncBeacon() { uint8_t setup_values[4]; setup_values[0] = 1; setup_values[1] = this->_tick_count; setup_values[2] = this->_tick_duration; setup_values[3] = this->_tx_offset; Serial.println("Sent beacon"); Wire.beginTransmission(_i2c_address); Wire.write((uint8_t *) setup_values, sizeof(setup_values)); Wire.endTransmission(); this->_first_sync = 0; _last_sync_time = t.getEpoch(); Serial.println(_last_sync_time); } String NoiseSensorClass::buildSenseBoxJSON(uint8_t device_id) { NoiseSensorNode* node = _nodes[device_id]; uint32_t ts = node->_sync_message_ts; // Each measurement line is max. 94 chars long + 2 curly brackets. uint16_t bufsize = _tick_count * 94 + 2; char json_buf[bufsize] = {0}; char sensor_id[25] = {0}; node->sensor_id.toCharArray(sensor_id, 25); // Build a json string in a very comprehensible way... strcat(json_buf, "[\n"); for (int i = 2; i < (_PACKET_SIZE - BATTERY_FIELD_SIZE); i++) { char tmpbuf[1024] = {0}; char timestamp[24] = {0}; float m = node->_measurements[i] / 10.0; // From fixed point to float. if (m >= 29 && m <= 120) { sprintf(timestamp, "%i-%02i-%02iT%02i:%02i:%02iZ", year(ts), month(ts), day(ts), hour(ts), minute(ts), second(ts)); sprintf(tmpbuf, "{\"sensor\":\"%s\", \"value\":\"%.1f\", \"createdAt\":\"%s\"}", sensor_id, m, timestamp); // No trailing comma on the last entry. if (i != _PACKET_SIZE - BATTERY_FIELD_SIZE - 1) { strcat(tmpbuf, ","); } strcat(json_buf, tmpbuf); strcat(json_buf, "\n"); } ts += this->_tick_duration; } /* if (strlen(json_buf) > 2) { node->missing_answers = 0; Serial.println("Missing answers reset."); } else { node->missing_answers++; Serial.print("Missing answers: "); Serial.println(node->missing_answers); } */ strcat(json_buf, "]"); return json_buf; } String NoiseSensorClass::buildHTTPHeader(uint8_t device_id, const char* server, uint16_t content_length) { char sb_id[25] = {0}; _nodes[device_id]->sensebox_id.toCharArray(sb_id, 25); char uploadbuf[2048] = {0}; sprintf(uploadbuf, "POST /boxes/%s/data HTTP/1.1\n" "Host: %s\n" "content-type: application/json\n" "Connection: close\n" "Content-Length: %u", sb_id, server, content_length); return String(uploadbuf); } // Private functions bool NoiseSensorClass::setupNTPClient() { configTime(0, 0, "de.pool.ntp.org"); // Allow processing of the package. delay(1000); struct tm timeinfo; if(!getLocalTime(&timeinfo)){ Serial.println("Failed to obtain time"); return false; } char timestamp[24]; uint32_t ts = t.getEpoch(); sprintf(timestamp, "%i-%02i-%02iT%02i:%02i:%02iZ", year(ts), month(ts), day(ts), hour(ts), minute(ts), second(ts)); Serial.println(timestamp); return true; } NoiseSensorClass NoiseSensor;