├── HB-UNI-Sen-LEV-US.ino ├── Images ├── CCU_Bedienung.png ├── CCU_Einstellungen.png ├── aussen.jpg ├── halter1.jpg ├── halter2.jpg ├── halter3.jpg ├── wiring.fzz ├── wiring.png └── wiring_w_temp.png ├── README.md ├── distance_test └── distance_test.ino └── docs ├── JSN-SR04T-2.0.pdf └── MB1040_Datasheet.pdf /HB-UNI-Sen-LEV-US.ino: -------------------------------------------------------------------------------- 1 | //- ----------------------------------------------------------------------------------------------------------------------- 2 | // AskSin++ 3 | // 2016-10-31 papa Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ 4 | // 2018-04-16 jp112sdl Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ 5 | // 2020-03-12 Wolfram Winter Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ 6 | //- ----------------------------------------------------------------------------------------------------------------------- 7 | // ci-test=yes board=328p aes=no 8 | 9 | // define this to read the device id, serial and device type from bootloader section 10 | // #define USE_OTA_BOOTLOADER 11 | 12 | #define USE_TEMPERATURE_COMPENSATION // connect DS18B20 Sensor to Pin 9 13 | #define SENSOR_ONLY 14 | 15 | #define EI_NOTEXTERNAL 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #ifdef USE_TEMPERATURE_COMPENSATION 23 | #include 24 | #include 25 | #endif 26 | 27 | // Arduino Pro mini 8 Mhz 28 | // Arduino pin for the config button 29 | #define CONFIG_BUTTON_PIN 8 30 | #define LED_PIN 4 31 | 32 | #define SENSOR_EN_PIN 5 //VCC Pin des Sensors 33 | #define SENSOR_ECHO_PIN 6 34 | #define SENSOR_TRIG_PIN 14 //A0 35 | #define BATT_EN_PIN 15 36 | #define BATT_SENS_PIN 17 37 | #define DS18B20_PIN 9 38 | 39 | #ifdef USE_TEMPERATURE_COMPENSATION 40 | //DS18B20 Sensors connected to pin 41 | OneWire oneWire(DS18B20_PIN); 42 | #endif 43 | 44 | // number of available peers per channel 45 | #define PEERS_PER_CHANNEL 2 46 | 47 | // all library classes are placed in the namespace 'as' 48 | using namespace as; 49 | 50 | //Korrekturfaktor der Clock-Ungenauigkeit, wenn keine RTC verwendet wird 51 | #define SYSCLOCK_FACTOR 0.88 52 | #define MAX_MEASURE_COUNT 5 53 | 54 | enum UltrasonicSensorTypes { 55 | JSN_SR04T_US100, 56 | MAXSONAR 57 | }; 58 | 59 | // define all device properties 60 | const struct DeviceInfo PROGMEM devinfo = { 61 | {0xF9, 0xD2, 0x01}, // Device ID 62 | "JPLEV00001", // Device Serial 63 | {0xF9, 0xD2}, // Device Model 64 | #ifdef USE_TEMPERATURE_COMPENSATION 65 | 0x12, // Firmware Version 66 | #else 67 | 0x11, 68 | #endif 69 | 0x53, // Device Type 70 | {0x01, 0x01} // Info Bytes 71 | }; 72 | 73 | /** 74 | Configure the used hardware 75 | */ 76 | typedef AskSin, BatterySensorUni, Radio, 2>> BaseHal; 77 | class Hal : public BaseHal { 78 | public: 79 | void init (const HMID& id) { 80 | BaseHal::init(id); 81 | battery.init(seconds2ticks(60UL * 60) * SYSCLOCK_FACTOR, sysclock); //battery measure once an hour 82 | battery.low(22); 83 | battery.critical(20); 84 | } 85 | 86 | bool runready () { 87 | return sysclock.runready() || BaseHal::runready(); 88 | } 89 | } hal; 90 | 91 | 92 | DEFREGISTER(UReg0, MASTERID_REGS, DREG_LOWBATLIMIT, 0x20, 0x21) 93 | class UList0 : public RegList0 { 94 | public: 95 | UList0 (uint16_t addr) : RegList0(addr) {} 96 | 97 | bool Sendeintervall (uint16_t value) const { 98 | return this->writeRegister(0x20, (value >> 8) & 0xff) && this->writeRegister(0x21, value & 0xff); 99 | } 100 | uint16_t Sendeintervall () const { 101 | return (this->readRegister(0x20, 0) << 8) + this->readRegister(0x21, 0); 102 | } 103 | 104 | void defaults () { 105 | clear(); 106 | lowBatLimit(22); 107 | Sendeintervall(180); 108 | } 109 | }; 110 | 111 | DEFREGISTER(UReg1, CREG_CASE_HIGH, CREG_CASE_WIDTH, CREG_CASE_DESIGN, CREG_CASE_LENGTH, 0x01, 0x02, 0x03) 112 | class UList1 : public RegList1 { 113 | public: 114 | UList1 (uint16_t addr) : RegList1(addr) {} 115 | 116 | bool distanceOffset (uint16_t value) const { 117 | return this->writeRegister(0x01, (value >> 8) & 0xff) && this->writeRegister(0x02, value & 0xff); 118 | } 119 | uint16_t distanceOffset () const { 120 | return (this->readRegister(0x01, 0) << 8) + this->readRegister(0x02, 0); 121 | } 122 | 123 | bool sensorType (uint16_t value) const { 124 | return this->writeRegister(0x03, value & 0xff); 125 | } 126 | uint16_t sensorType () const { 127 | return this->readRegister(0x03, 0); 128 | } 129 | 130 | void defaults () { 131 | clear(); 132 | caseHigh(100); 133 | caseWidth(100); 134 | caseLength(100); 135 | caseDesign(0); 136 | distanceOffset(0); 137 | sensorType(0); 138 | } 139 | }; 140 | 141 | class MeasureEventMsg : public Message { 142 | public: 143 | void init(uint8_t msgcnt, uint8_t percent, uint32_t liter, uint16_t height, uint8_t volt, int16_t temp) { 144 | // Message Length (first byte param.): 11 + payload = 17 = 0x11 145 | uint8_t msg_len = 0x11; 146 | #ifdef USE_TEMPERATURE_COMPENSATION 147 | msg_len += 0x03; // three extra bytes for the temperature channel 148 | #endif 149 | Message::init(msg_len, msgcnt, 0x53, BIDI | WKMEUP, percent & 0xff, volt & 0xff); 150 | pload[0] = (liter >> 24) & 0xff; 151 | pload[1] = (liter >> 16) & 0xff; 152 | pload[2] = (liter >> 8) & 0xff; 153 | pload[3] = liter & 0xff; 154 | pload[4] = (height >> 8) & 0xff; 155 | pload[5] = height & 0xff; 156 | #ifdef USE_TEMPERATURE_COMPENSATION 157 | pload[6] = 0x42; 158 | pload[7] = (temp >> 8) & 0xff; 159 | pload[8] = temp & 0xff; 160 | #endif 161 | } 162 | }; 163 | 164 | class MeasureChannel : public Channel, public Alarm { 165 | MeasureEventMsg msg; 166 | uint8_t fillingPercent; 167 | uint32_t fillingLiter; 168 | uint16_t fillingHeight; 169 | uint16_t distance; 170 | int16_t temperature; 171 | uint8_t sensorCount; 172 | 173 | uint8_t last_flags = 0xff; 174 | 175 | #ifdef USE_TEMPERATURE_COMPENSATION 176 | Ds18b20 tempSensors[1]; 177 | #endif 178 | 179 | public: 180 | MeasureChannel () : Channel(), Alarm(0), fillingPercent(0), fillingLiter(0), fillingHeight(0), distance(0), temperature(-600), sensorCount(0) {} 181 | virtual ~MeasureChannel () {} 182 | 183 | void measure() { 184 | uint32_t caseHeight = this->getList1().caseHigh(); 185 | uint32_t caseWidth = this->getList1().caseWidth(); 186 | uint32_t caseLength = this->getList1().caseLength(); 187 | uint32_t caseDesign = this->getList1().caseDesign(); 188 | uint32_t distanceOffset = this->getList1().distanceOffset(); 189 | 190 | uint32_t m_value = 0; 191 | uint32_t duration = 0; 192 | uint8_t validcnt = 0; 193 | 194 | 195 | pinMode(SENSOR_ECHO_PIN, INPUT_PULLUP); 196 | _delay_ms(300); 197 | digitalWrite(SENSOR_EN_PIN, HIGH); 198 | _delay_ms(300); 199 | switch (this->getList1().sensorType()) { 200 | case JSN_SR04T_US100: 201 | { 202 | digitalWrite(SENSOR_TRIG_PIN, LOW); 203 | delayMicroseconds(2); 204 | digitalWrite(SENSOR_TRIG_PIN, HIGH); 205 | delayMicroseconds(10); 206 | digitalWrite(SENSOR_TRIG_PIN, LOW); 207 | 208 | duration = pulseIn(SENSOR_ECHO_PIN, HIGH); 209 | 210 | validcnt++; 211 | } 212 | break; 213 | case MAXSONAR: 214 | { 215 | pulseIn(SENSOR_ECHO_PIN, HIGH); 216 | _delay_ms(100); 217 | for (uint8_t i = 0; i < MAX_MEASURE_COUNT; i++) { 218 | uint16_t p = pulseIn(SENSOR_ECHO_PIN, HIGH); 219 | if (p < 35750) { 220 | duration += p; 221 | validcnt++; 222 | } else { 223 | DPRINTLN("Invalid range detected!"); 224 | } 225 | _delay_ms(100); 226 | } 227 | 228 | } 229 | break; 230 | default: 231 | DPRINTLN(F("Invalid Sensor Type selected")); 232 | break; 233 | } 234 | 235 | digitalWrite(SENSOR_EN_PIN, LOW); 236 | pinMode(SENSOR_ECHO_PIN, INPUT); 237 | 238 | duration = (validcnt > 0) ? duration / validcnt : 0; 239 | 240 | m_value = (duration * 1000L / 57874L); 241 | 242 | DPRINT("DIST UNCOMP = "); DDECLN(m_value); 243 | #ifdef USE_TEMPERATURE_COMPENSATION 244 | if (sensorCount > 0) { 245 | Ds18b20::measure(tempSensors, sensorCount); 246 | temperature = tempSensors[0].temperature(); 247 | DPRINT("TEMPERATURE = ");DDECLN(temperature); 248 | uint32_t speedOfSound = 3313L + (606L * temperature) / 1000L; 249 | uint32_t compensatedDistance = ((duration * 100000L / 20000) * speedOfSound) / 1000000L; 250 | DPRINT("DIST COMP = "); DDECLN(compensatedDistance); 251 | m_value = compensatedDistance; 252 | } 253 | #endif 254 | DPRINTLN(""); 255 | DPRINT(F("Abstand gemessen : ")); DDECLN(m_value); 256 | distance = (distanceOffset > m_value) ? 0 : m_value - distanceOffset; 257 | DPRINT(F("Abstand abzgl. OFFSET : ")); DDECLN(distance); 258 | 259 | DPRINT(F("Behaelterhoehe : ")); DDECLN(caseHeight); 260 | fillingHeight = (distance > caseHeight) ? 0 : caseHeight - distance; 261 | DPRINT(F("Fuellhoehe : ")); DDECLN(fillingHeight); 262 | 263 | uint32_t caseVolume = 0; float r = 0; 264 | switch (caseDesign) { 265 | case 0: 266 | caseVolume = (PI * pow((caseWidth >> 1), 2) * caseHeight) / 1000L; 267 | fillingLiter = (PI * pow((caseWidth >> 1), 2) * fillingHeight) / 1000L; 268 | break; 269 | case 1: 270 | caseVolume = (PI * pow((caseHeight >> 1), 2) * caseWidth) / 1000L; 271 | //fillingLiter = pow(caseHeight >> 1, 2) * caseWidth * (acos((caseHeight >> 1 - fillingHeight) / caseHeight >> 1) - (caseHeight >> 1 - fillingHeight) * (sqrt((2 * caseHeight >> 1 * fillingHeight) - pow(fillingHeight, 2)) / pow(caseHeight >> 1, 2))) ; 272 | r = caseHeight / 2; 273 | fillingLiter = (r * r * 2 * acos(1 - fillingHeight / r) / 2 - 2 * sqrt(caseHeight * fillingHeight - fillingHeight * fillingHeight) * (r - fillingHeight) / 2) * caseWidth / 1000L; 274 | break; 275 | case 2: 276 | caseVolume = (caseHeight * caseWidth * caseLength) / 1000L; 277 | fillingLiter = (fillingHeight * caseWidth * caseLength) / 1000L; 278 | break; 279 | default: 280 | DPRINTLN(F("Invalid caseDesign")); DDECLN(caseDesign); 281 | break; 282 | } 283 | 284 | fillingPercent = (fillingLiter * 100) / caseVolume; 285 | DPRINT(F("Behaeltervolumen (gesamt): ")); DDECLN(caseVolume); 286 | DPRINT(F("Inhalt : ")); DDEC(fillingLiter); DPRINT(F("L (")); DDEC(fillingPercent); DPRINTLN(F("%)")); 287 | DPRINTLN(""); 288 | } 289 | virtual void trigger (__attribute__ ((unused)) AlarmClock & clock) { 290 | uint8_t msgcnt = device().nextcount(); 291 | if (last_flags != flags()) { 292 | this->changed(true); 293 | last_flags = flags(); 294 | } 295 | measure(); 296 | tick = delay(); 297 | msg.init(msgcnt, fillingPercent, fillingLiter, fillingHeight, device().battery().current(), temperature); 298 | if (msgcnt % 20 == 1) device().sendPeerEvent(msg, *this); else device().broadcastEvent(msg); 299 | sysclock.add(*this); 300 | } 301 | 302 | uint32_t delay () { 303 | uint16_t _txMindelay = 20; 304 | _txMindelay = device().getList0().Sendeintervall(); 305 | if (_txMindelay == 0) _txMindelay = 20; 306 | return seconds2ticks(_txMindelay * SYSCLOCK_FACTOR); 307 | } 308 | 309 | void configChanged() { 310 | DPRINTLN(F("Config changed List1")); 311 | DPRINT(F("*CASE_HIGH: ")); 312 | DDECLN(this->getList1().caseHigh()); 313 | DPRINT(F("*CASE_WIDTH: ")); 314 | DDECLN(this->getList1().caseWidth()); 315 | DPRINT(F("*CASE_LENGTH: ")); 316 | DDECLN(this->getList1().caseLength()); 317 | DPRINT(F("*CASE_DESIGN: ")); 318 | DDECLN(this->getList1().caseDesign()); 319 | DPRINT(F("*DISTANCE_OFFSET: ")); 320 | DDECLN(this->getList1().distanceOffset()); 321 | DPRINT(F("*SENSOR_TYPE: ")); 322 | DDECLN(this->getList1().sensorType()); 323 | } 324 | 325 | void setup(Device* dev, uint8_t number, uint16_t addr) { 326 | Channel::setup(dev, number, addr); 327 | // pinMode(SENSOR_ECHO_PIN, INPUT_PULLUP); 328 | pinMode(SENSOR_ECHO_PIN, INPUT); 329 | pinMode(SENSOR_TRIG_PIN, OUTPUT); 330 | pinMode(SENSOR_EN_PIN, OUTPUT); 331 | 332 | #ifdef USE_TEMPERATURE_COMPENSATION 333 | sensorCount = Ds18b20::init(oneWire, tempSensors, 1); 334 | DPRINT("Found "); DDEC(sensorCount); DPRINTLN(" DS18B20 Sensor(s)"); 335 | #endif 336 | sysclock.add(*this); 337 | } 338 | 339 | uint8_t status () const { 340 | return 0; 341 | } 342 | 343 | uint8_t flags () const { 344 | uint8_t flags = 0x00; 345 | 346 | #ifdef USE_TEMPERATURE_COMPENSATION 347 | if (sensorCount == 0) flags = 0x01 << 1; 348 | #endif 349 | 350 | flags |= this->device().battery().low() ? 0x80 : 0x00; 351 | return flags; 352 | } 353 | }; 354 | 355 | class UType : public MultiChannelDevice { 356 | public: 357 | typedef MultiChannelDevice TSDevice; 358 | UType(const DeviceInfo& info, uint16_t addr) : TSDevice(info, addr) {} 359 | virtual ~UType () {} 360 | 361 | virtual void configChanged () { 362 | TSDevice::configChanged(); 363 | DPRINT(F("*LOW BAT Limit: ")); 364 | DDECLN(this->getList0().lowBatLimit()); 365 | this->battery().low(this->getList0().lowBatLimit()); 366 | DPRINT(F("*Sendeintervall: ")); DDECLN(this->getList0().Sendeintervall()); 367 | } 368 | }; 369 | 370 | UType sdev(devinfo, 0x20); 371 | ConfigButton cfgBtn(sdev); 372 | 373 | void setup () { 374 | DINIT(57600, ASKSIN_PLUS_PLUS_IDENTIFIER); 375 | sdev.init(hal); 376 | buttonISR(cfgBtn, CONFIG_BUTTON_PIN); 377 | DDEVINFO(sdev); 378 | sdev.initDone(); 379 | } 380 | 381 | void loop() { 382 | bool worked = hal.runready(); 383 | bool poll = sdev.pollRadio(); 384 | if ( worked == false && poll == false ) { 385 | if ( hal.battery.critical() ) { 386 | hal.activity.sleepForever(hal); 387 | } 388 | hal.activity.savePower>(hal); 389 | } 390 | } 391 | -------------------------------------------------------------------------------- /Images/CCU_Bedienung.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jp112sdl/HB-UNI-Sen-LEV-US/141bae5dcd5752278abaf37f5af18fdfcb4dfb09/Images/CCU_Bedienung.png -------------------------------------------------------------------------------- /Images/CCU_Einstellungen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jp112sdl/HB-UNI-Sen-LEV-US/141bae5dcd5752278abaf37f5af18fdfcb4dfb09/Images/CCU_Einstellungen.png -------------------------------------------------------------------------------- /Images/aussen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jp112sdl/HB-UNI-Sen-LEV-US/141bae5dcd5752278abaf37f5af18fdfcb4dfb09/Images/aussen.jpg -------------------------------------------------------------------------------- /Images/halter1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jp112sdl/HB-UNI-Sen-LEV-US/141bae5dcd5752278abaf37f5af18fdfcb4dfb09/Images/halter1.jpg -------------------------------------------------------------------------------- /Images/halter2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jp112sdl/HB-UNI-Sen-LEV-US/141bae5dcd5752278abaf37f5af18fdfcb4dfb09/Images/halter2.jpg -------------------------------------------------------------------------------- /Images/halter3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jp112sdl/HB-UNI-Sen-LEV-US/141bae5dcd5752278abaf37f5af18fdfcb4dfb09/Images/halter3.jpg -------------------------------------------------------------------------------- /Images/wiring.fzz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jp112sdl/HB-UNI-Sen-LEV-US/141bae5dcd5752278abaf37f5af18fdfcb4dfb09/Images/wiring.fzz -------------------------------------------------------------------------------- /Images/wiring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jp112sdl/HB-UNI-Sen-LEV-US/141bae5dcd5752278abaf37f5af18fdfcb4dfb09/Images/wiring.png -------------------------------------------------------------------------------- /Images/wiring_w_temp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jp112sdl/HB-UNI-Sen-LEV-US/141bae5dcd5752278abaf37f5af18fdfcb4dfb09/Images/wiring_w_temp.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HB-UNI-Sen-LEV-US 2 | #### Füllstandsensor für HomeMatic 3 | 4 | Um die Geräteunterstützung zu aktivieren, wird die aktuellste Version des [JP-HB-Devices Addon](https://github.com/jp112sdl/JP-HB-Devices-addon/releases/latest) benötigt! 5 |
6 |
7 | _Der erste Prototyp_ 8 | 9 | #### Bauteile 10 | - Batteriehalter für 3 oder 4 Batterien 11 | - Ultraschallsensor: 12 | - z.B. [JSN-SR04T V2.0](https://de.aliexpress.com/item/Integrated-Ultrasonic-Module-Distance-Measuring-Sensor-Module-Reversing-Radar-Waterproof/32312190912.html) 13 | - **Hinweis:** Unbedingt die **V2.0** nehmen, weil diese auch mit 3.3V zuverlässig arbeitet! 14 | - oder [US-100](https://eckstein-shop.de/Adafruit-US-100-Ultrasonic-Distance-Sensor-3V-or-5V-Logic) 15 | - oder MaxBotix [MaxSonar MB1040, LV-MaxSonar-EZ4](https://de.aliexpress.com/item/NEW-Mini-uav-obstacle-avoidance-ultrasonic-ranging-module-MB1040-High-Performance-Sonar-Range-Finder/32862912642.html) 16 | 17 | - [CC1101 (868MHz)](https://de.aliexpress.com/item/CC1101-Wireless-Module-Long-Distance-Transmission-Antenna-868MHZ-M115-For-FSK-GFSK-ASK-OOK-MSK-64/32635393463.html) 18 | - [Arduino Pro Mini 3.3V/8MHz](https://de.aliexpress.com/item/Free-Shipping-1pcs-pro-mini-atmega328-Pro-Mini-328-Mini-ATMEGA328-3-3V-8MHz-for-Arduino/32342672626.html) 19 | - Kleinteile... 20 | - 1x LED 21 | - 1x Taster 22 | - Widerstände, je 1x 330 Ohm, 100kOhm, 470kOhm 23 | 24 | #### Verdrahtung 25 | ![wiring](Images/wiring.png) 26 | 27 | **oder mit Temperaturkompensation:** 28 | ![wiring_w_temp](Images/wiring_w_temp.png) 29 | 30 | **Bei Verwendung des MaxSonar Sensors ist der PW-Pin an den SENSOR_ECHO_PIN anzuschließen. Der SENSOR_TRIG_PIN wird nicht verwendet.** 31 | 32 | #### CCU Geräteeinstellungen 33 | ![ccu_setting](Images/CCU_Einstellungen.png) 34 | 35 | Bei Verwendung des **US-100** ist der **JSN-SR04T** auszuwählen 36 | 37 | #### CCU Anzeige der Werte 38 | ![ccu_setting](Images/CCU_Bedienung.png) 39 | 40 | #### Halterung / Aufhängung 41 | 42 | 43 | 44 | 45 | **Die Teile findet ihr bei Tinkercad:** 46 |
47 | [Stange für Höhe 20cm](https://www.tinkercad.com/things/iM7Yyc3prMh-ultraschallsensor-halterung-stange-20cm-hohe/editv2)
48 | [Stange für Höhe 15cm](https://www.tinkercad.com/things/7xHuCkIllsB-ultraschallsensor-halterung-stange-15cm/editv2)
49 | [Ringe](https://www.tinkercad.com/things/15f2ttggOnX-ultraschallsensor-halterung/editv2)
50 | [Deckel](https://www.tinkercad.com/things/2bYtNM8HQ3f-ultraschallsensor-halterung-deckel/editv2) 51 | -------------------------------------------------------------------------------- /distance_test/distance_test.ino: -------------------------------------------------------------------------------- 1 | const int pwPin1 = 6; 2 | long sensor; 3 | 4 | void setup() { 5 | Serial.begin(57600); 6 | pinMode(5, OUTPUT); 7 | pinMode(pwPin1, INPUT); 8 | } 9 | 10 | void read_sensor () { 11 | digitalWrite(5, HIGH); 12 | delay(300); 13 | sensor = pulseIn(pwPin1, HIGH); 14 | digitalWrite(5, LOW); 15 | 16 | } 17 | 18 | void print_range() { 19 | Serial.print("S1"); 20 | Serial.print("="); 21 | Serial.print( sensor); 22 | Serial.print(", "); 23 | Serial.println( (sensor * 1000L / 57874L)); 24 | } 25 | 26 | void loop() { 27 | read_sensor(); 28 | print_range(); 29 | delay(100); 30 | } 31 | -------------------------------------------------------------------------------- /docs/JSN-SR04T-2.0.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jp112sdl/HB-UNI-Sen-LEV-US/141bae5dcd5752278abaf37f5af18fdfcb4dfb09/docs/JSN-SR04T-2.0.pdf -------------------------------------------------------------------------------- /docs/MB1040_Datasheet.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jp112sdl/HB-UNI-Sen-LEV-US/141bae5dcd5752278abaf37f5af18fdfcb4dfb09/docs/MB1040_Datasheet.pdf --------------------------------------------------------------------------------