// vim: noai:sw=2:tw=88 #include #include "NoiseSensor.h" //#include "NoiseSensorNode.h" 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."); } else if (this->_tick_count < 10) { Serial.println( "--> Using a tick count smaller than 10 might screw things up in a " "unpredictable manner and is not recommended."); } if (this->_tick_count >= 120) { Serial.println( "--> Increased the tick count over 120. This results in I²C packages of " "(tick_count + 3 ) * 2. " "Note that I²C packets longer than 256 bytes will need to be split into " "smaller chunks to allow them to transfer." } if (!setupNTPClient()) { Serial.println("!!!-------------------------!!!"); Serial.println("NTPClient couldn't be started."); Serial.println("!!!-------------------------!!!"); return false; } I2C.begin(400000); this->sendSyncBeacon(); while (I2C.writeBusy) { delay(250); Serial.println("Writing!"); } 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 (this->_time_client->getEpochTime() - this->_last_sync_time == if (now() - _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 && //this->_time_client->getEpochTime() - this->_last_sync_time == now() - _last_sync_time == (5 * this->_tx_offset * this->_tick_duration + 1)) { Serial.println("---->Request ready"); return true; } return false; } bool NoiseSensorClass::read() { Serial.println("Starting read"); for (int pkt = 0; pkt < PACKET_COUNT; pkt++) { // Calculate the time of the sync message that triggered this packet. uint32_t time_offset = (_tick_count * _tick_duration) + 1; // TODO Move this->_nodes[pkt]->_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 (_nodes[pkt]->missing_answers > 0) { _nodes[pkt]->_sync_message_ts -= time_offset * _nodes[pkt]->missing_answers; } */ I2C.readBytes(this->_i2c_address, (uint8_t *) this->_nodes[pkt]->_measurements, this->_PACKET_SIZE * 2); } uint32_t read_start_time = millis(); while (I2C.readBusy) { Serial.println("Reading!"); delay(1); // Restart I²C if no response for more than a second. if (millis() - read_start_time >= 1000) { Serial.println("Restarting I²C"); I2C.end(); I2C.begin(400000); return false; } } // 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++) { sprintf(printbuf, "%02i:%02i:%02i -> ", hour(ts), minute(ts), second(ts)); Serial.print(printbuf); Serial.println(this->_nodes[node_id]->_measurements[i]); ts += this->_tick_duration; } } bool NoiseSensorClass::sendSyncBeacon() { //_last_sync_time = _time_client->getEpochTime(); uint8_t setup_values[3]; setup_values[0] = this->_tick_count; setup_values[1] = this->_tick_duration; setup_values[2] = this->_tx_offset; Serial.println("Sent beacon"); I2C.writeBytes(this->_i2c_address, 0x01, (uint8_t *)setup_values, sizeof(setup_values)); this->_first_sync = 0; _last_sync_time = now(); Serial.println(_last_sync_time); Serial.print(minute(_last_sync_time)); Serial.print(":"); Serial.println(second(_last_sync_time)); while (I2C.writeBusy) { delay(250); Serial.println("Writing!"); } } String NoiseSensorClass::buildSenseBoxJSON(uint8_t device_id) { // _time_client->update(); 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() { uint32_t start_time = millis(); while (timeStatus() != timeSet) { if (millis() - start_time >= 200) { return false; } } return true; //setSyncProvider( //WiFiUDP ntpUDP; //this->_time_client = new NTPClient(ntpUDP); //this->_time_client->begin(); //if (this->_time_client->update()) { return true; } } NoiseSensorClass NoiseSensor;