Skip to content
  • MySensors
  • OpenHardware.io
  • Categories
  • Recent
  • Tags
  • Popular
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Brand Logo
  1. Home
  2. My Project
  3. Mysensorized Roomba

Mysensorized Roomba

Scheduled Pinned Locked Moved My Project
18 Posts 9 Posters 7.4k Views 10 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • E Eawo

    Maybe this is off topic but I'm going to buy a roomba but it is so many versions. Will roomba 615 or 616 do the job or is the newer versions smarter cleaner? I have around 80kvd

    parachutesjP Offline
    parachutesjP Offline
    parachutesj
    wrote on last edited by
    #9

    @Eawo I do have a 581 and not 100% sure what has been changed on newer models. Maybe you ask in a roomba forum

    parachutesjP 1 Reply Last reply
    0
    • parachutesjP parachutesj

      @Eawo I do have a 581 and not 100% sure what has been changed on newer models. Maybe you ask in a roomba forum

      parachutesjP Offline
      parachutesjP Offline
      parachutesj
      wrote on last edited by
      #10

      All,
      I did some updates also thanks to @Eoreh I updated my code a bit. Anyhow, no really news on software side but was able to fit it more or less completely inside of the Roomba.

      1. I wanted to fit all inside and I think there is enough space but wanted to be able to disable it and thought about a dip-switch somewhere but then decided to have it connected via DIN as before. Means also no soldering on Roomba

      2. finding a spot was easy:
        alt text
        There is enough space on the side of the bin. (In picture, the external charging port was already removed)

      3. I soldered all together and decided to opt for a Nano instead of a Pro Mini due to the lack of 3.3V on board. I know radio might be better powered with a voltage regulator but the reception and reliability is not too important here. Anyhow not experienced any issues so far. I also used the SMD radio to save some extra space.
        The voltage regulator has a heat sink mounted which is pointing where the charing port was and is getting some "fresh air" ;-)
        alt text

      4. all in place and ready to be closed again
        alt text

      5. closed without top plate
        alt text

      If it stops working or needs a reset, I only need to unplug and replug

      M 1 Reply Last reply
      0
      • parachutesjP parachutesj

        All,
        I did some updates also thanks to @Eoreh I updated my code a bit. Anyhow, no really news on software side but was able to fit it more or less completely inside of the Roomba.

        1. I wanted to fit all inside and I think there is enough space but wanted to be able to disable it and thought about a dip-switch somewhere but then decided to have it connected via DIN as before. Means also no soldering on Roomba

        2. finding a spot was easy:
          alt text
          There is enough space on the side of the bin. (In picture, the external charging port was already removed)

        3. I soldered all together and decided to opt for a Nano instead of a Pro Mini due to the lack of 3.3V on board. I know radio might be better powered with a voltage regulator but the reception and reliability is not too important here. Anyhow not experienced any issues so far. I also used the SMD radio to save some extra space.
          The voltage regulator has a heat sink mounted which is pointing where the charing port was and is getting some "fresh air" ;-)
          alt text

        4. all in place and ready to be closed again
          alt text

        5. closed without top plate
          alt text

        If it stops working or needs a reset, I only need to unplug and replug

        M Offline
        M Offline
        moskovskiy82
        wrote on last edited by moskovskiy82
        #11

        @parachutesj Mind posting your finished code?

        Also wonder what is the correct MQTT payload to start for example cleaning?

        Upon start i see the following

        mys-out/15/255/3/0/11 MYS Roomba
        mys-out/15/255/3/0/12 2.1
        mys-out/15/0/0/0/3 Roomba-Stop
        mys-out/15/1/0/0/3 Roomba-Dock
        mys-out/15/2/0/0/3 Roomba-Clean
        mys-out/15/3/0/0/3 Roomba-Spot
        mys-out/15/4/0/0/3 Roomba-Max
        mys-out/15/5/0/0/3 Roomba-Power
        mys-out/15/6/0/0/3 Roomba-Play
        mys-out/15/100/0/0/6 Roomba-Batt-Temp
        mys-out/15/101/0/0/39 Roomba-Batt-Proc
        mys-out/15/102/0/0/30 Roomba-Batt-Volt
        mys-out/15/103/0/0/30 Roomba-Batt-mAH
        mys-out/15/104/0/0/36 Roomba-Batt-CHt
        mys-out/15/105/0/0/36 Roomba-Batt-CHs

        But publishing to

        mys-in/15/2/1/0/3
        mys-in/15/2/1/0/2

        with payload 1 doesn't do anything

        parachutesjP 1 Reply Last reply
        0
        • M moskovskiy82

          @parachutesj Mind posting your finished code?

          Also wonder what is the correct MQTT payload to start for example cleaning?

          Upon start i see the following

          mys-out/15/255/3/0/11 MYS Roomba
          mys-out/15/255/3/0/12 2.1
          mys-out/15/0/0/0/3 Roomba-Stop
          mys-out/15/1/0/0/3 Roomba-Dock
          mys-out/15/2/0/0/3 Roomba-Clean
          mys-out/15/3/0/0/3 Roomba-Spot
          mys-out/15/4/0/0/3 Roomba-Max
          mys-out/15/5/0/0/3 Roomba-Power
          mys-out/15/6/0/0/3 Roomba-Play
          mys-out/15/100/0/0/6 Roomba-Batt-Temp
          mys-out/15/101/0/0/39 Roomba-Batt-Proc
          mys-out/15/102/0/0/30 Roomba-Batt-Volt
          mys-out/15/103/0/0/30 Roomba-Batt-mAH
          mys-out/15/104/0/0/36 Roomba-Batt-CHt
          mys-out/15/105/0/0/36 Roomba-Batt-CHs

          But publishing to

          mys-in/15/2/1/0/3
          mys-in/15/2/1/0/2

          with payload 1 doesn't do anything

          parachutesjP Offline
          parachutesjP Offline
          parachutesj
          wrote on last edited by
          #12

          @moskovskiy82 I did not change too much compared to @EmielA on the code. worked pretty good until I bought a new roomba recently. Haven't tried it on the 980 yet:

          // Enable debug prints to serial monitor
          #define MY_DEBUG
          
          // Enable and select radio type attached
          #define MY_RADIO_NRF24
          //#define MY_RADIO_RFM69
          
          // Enable repeater functionality for this node
          #define MY_REPEATER_FEATURE
          
          #define CHILD_ID_BATT_TEMP          100  // Id of the 2nd msg1_3
          #define CHILD_ID_BATT_PROC          101   // Id of the 2nd msg1_4
          #define CHILD_ID_BATT_VOLT          102   // Id of the 2nd msg1_2
          #define CHILD_ID_BATT_AH            103   // Id of the 2nd msg2_5
          #define CHILD_ID_BATT_CHARGE_TYPE   104   // Id of the 2nd msg1_1
          #define CHILD_ID_BATT_CHARGE_SOURCE 105   // Id of the 2nd msg2_2
          
          
          #include <MySensors.h>
          #include <SoftwareSerial.h>
          
          //#define RELAY_1  5  // Arduino Digital I/O pin number for first relay (second on pin+1 etc)
          //#define NUMBER_OF_RELAYS 2 // Total number of attached relays
          //#define RELAY_ON 1  // GPIO value to write to turn on attached relay
          //#define RELAY_OFF 0 // GPIO value to write to turn off attached relay
          
          #define NUMBER_OF_FUNCTION 7 
          #define NUMBER_OF_SENSORS 6
          #define ROOMBA_READ_TIMEOUT 300
          #define ROOMBA_WAKE_COUNT 10
          int ddPin = 6;
          
          int rxPin = 3;
          int txPin = 4;
          int ledPin = 13;
          
          SoftwareSerial roomba(rxPin, txPin);
          
          const char SWITCH_NAMES[NUMBER_OF_FUNCTION][14] = { "Roomba-Stop","Roomba-Dock","Roomba-Clean","Roomba-Spot","Roomba-Max","Roomba-Power","Roomba-Play" };
          const char SENSORS_NAMES[NUMBER_OF_SENSORS][24] = { "Roomba-Batt-Temp","Roomba-Batt-Proc","Roomba-Batt-Volt", "Roomba-Batt-mAH" , "Roomba-Batt-CHt" , "Roomba-Batt-CHs" };
          
          
          uint8_t buf[52];
          boolean lowBattery = true; // Acts as a debounce
          long battery_Current_mAh = 0;
          long battery_Total_mAh = 0;
          long battery_percent = 0;
          boolean DeadBattery = false;
          boolean FullBattery = true;
          unsigned long chargingState = 0;
          long voltage = 0;
          long temp = 0;
          long OiMode = 0;
          long ChargeSource = 0;
          
          long sensorfrequency = 300000; // 300000 = 5 min - frequency the sensors are read
          long lastsensorread = 0; // when were the sensors read the last time
          unsigned long lastsensorssend = 0;
          
          const char ChargeTypes[6][34] = { "Not charging","Reconditioning Charging","Full Charging","Trickle Charging","Waiting","Charging Fault Condition" };
          const char OiTypes[4][14] = { "Off","Passive","Safe","Full" };
          
          int packetSizes[58] = {
              0,0,0,0,0,0, //1-6
              1,1,1,1,1,1,1,1,1,1,1,1, //7-18
              2,2, //19-20
              1, //21
              2,2, //22-23
              1, //24
              2,2,2,2,2,2,2, //25-31
              1, //32
              2, //33
              1,1,1,1,1, //34-38
              2,2,2,2,2,2, //39-44
              1, //45
              2,2,2,2,2,2, //46-51
              1,1, //52-53
              2,2,2,2, //54-57
              1 //58
          };
          #define bumpright (sensorbytes[0] & 0x01)
          #define bumpleft  (sensorbytes[0] & 0x02)
          
          
          MyMessage msg1_3(CHILD_ID_BATT_TEMP, V_TEMP); //status of Temperature
          MyMessage msg1_4(CHILD_ID_BATT_PROC, V_PH); //status of Charge [%]
          MyMessage msg1_2(CHILD_ID_BATT_VOLT, V_VOLTAGE); //status of Voltage
          MyMessage msg2_5(CHILD_ID_BATT_AH, V_CURRENT); //status of mAH
          MyMessage msg1_1(CHILD_ID_BATT_CHARGE_TYPE, V_TEXT); //status of charget type
          MyMessage msg2_2(CHILD_ID_BATT_CHARGE_SOURCE, V_TEXT); //status of charger source
          
          void setup() {
              pinMode(ddPin, OUTPUT);
              digitalWrite(ddPin, LOW);
              Serial.begin(115200);
              while (!Serial) {
                  ; // wait for serial port to connect. Needed for native USB port only
              }
              roomba.begin(115200);
              //Serial.println("Sending start command...");
              //delay(1000);
              //// set up ROI to receive commands
              defsongs();
              delay(500);
              rstop();
              Serial.println("Ready to go!");
          }
          
          void presentation()
          {
              // Send the sketch version information to the gateway and Controller
              sendSketchInfo("Roomba", "1.2");
          
              for (int sensor = 0; sensor < NUMBER_OF_FUNCTION; sensor++) {
                  // Register all sensors to gw (they will be created as child devices)
                  present(sensor, S_BINARY, SWITCH_NAMES[sensor]);
                  delay(100);
              }
              present(CHILD_ID_BATT_TEMP, S_TEMP, SENSORS_NAMES[0]); delay(100);
              present(CHILD_ID_BATT_PROC, S_WATER_QUALITY, SENSORS_NAMES[1]); delay(100);
              present(CHILD_ID_BATT_VOLT, S_MULTIMETER, SENSORS_NAMES[2]);    delay(100);
              present(CHILD_ID_BATT_AH, S_MULTIMETER, SENSORS_NAMES[3]);  delay(100);
              present(CHILD_ID_BATT_CHARGE_TYPE, S_INFO, SENSORS_NAMES[4]); delay(100);
              present(CHILD_ID_BATT_CHARGE_SOURCE, S_INFO, SENSORS_NAMES[5]); delay(100);
          }
          
          void loop() {
              unsigned long now = millis();
              if (now > lastsensorssend + sensorfrequency)
              {
                  readsensors();
                  lastsensorssend = now;
              }
          }
          
          
          void receive(const MyMessage &message) {
            // We only expect one type of message from controller. But we better check anyway.
            if (message.type == V_LIGHT) {
              int mySensor = message.sensor;
              int myComand = message.getBool();
          
              //Serial.print(message.sensor-1+RELAY_1);
          
              if (mySensor == 0) {
                if (myComand == 1) {
                  Serial.println("Clean");
                  //clean();
                  rcommand(135);
                }
                else {
                  Serial.println("Stopp1");
                  //halt();
                  rstop();
                }
              }
          
              if (mySensor == 1) {
                if (myComand == 1) {
                  Serial.println("dock");
                  //dock();
                  rcommand(143);
                }
                else {
                  Serial.println("Stopp2");
                  //halt();
                  rstop();
                }
              }
              if (mySensor == 2) {
                if (myComand == 1) {
                  Serial.println("song");
                  //dock();
                  rcommand(141);
                  rsong(0);
                }
                else {
                  Serial.println("Stopp2");
                  //halt();
                  rstop();
                }
              }
          
            }
          }
          
          void halt() {
            ;
            roomba.write(131);  // SAFE
            byte j = 0x00;
            roomba.write(137);
            roomba.write(j);
            roomba.write(j);
            roomba.write(j);
            roomba.write(j);
          }
          
          void dock() {
            delay(1000);
            roomba.write(143);
          }
          
          void clean() {
            delay(1000);
            roomba.write(135);
          }
          
          void rstop() {
              if (rwakeup()) {
                  roomba.write(128);  delay(100);
                  roomba.write(131);  delay(100);
                  roomba.write(137);  delay(100);
                  byte j = 0x00;
                  roomba.write(j); delay(100);
                  roomba.write(j); delay(100);
                  roomba.write(j); delay(100);
                  roomba.write(j); delay(100);
                  roomba.write(128);
              }
          
          }
          
          void rsong(int b) {
              roomba.write(b); delay(100);
          }
          
          
          
          void rcommand(int command) {
              if (rwakeup()) {
                  roomba.write(128);  delay(100);
                  roomba.write(131);  delay(100);
                  roomba.write(command);
              }
          }
          
          void play(char note, int rlong) {
              roomba.write(rnote(note));  delay(10);  roomba.write(64 / rlong);   delay(10);
          }
          
          
          bool rwakeup() {
              bool ret = getSensors(6, buf, 52);
              Serial.print("wakeup: ");
              Serial.print(ret);
              if (!ret) {
                  for (int ii = 1; ii < ROOMBA_WAKE_COUNT; ii++)
                  {
                      for (int jj = 0; jj < 3; jj++) {
                          digitalWrite(ddPin, HIGH); delay(10); 
                          digitalWrite(ddPin, LOW); delay(2);
                      }
                      delay(100);
                      roomba.write(128);
                      if (getSensors(6, buf, 52)) {
                          ret = true;
                          break;
                      }
                      Serial.print("-");
                  }
              }
              Serial.println(ret);
              return ret;
          }
          
          bool getSensors(uint8_t packetID, uint8_t* dest, uint8_t len) {
              roomba.write(142);
              delay(100);
              roomba.write(packetID);
              delay(100);
              return getData(dest, len);
          }
          
          
          bool getData(uint8_t* dest, uint8_t len)
          {
              while (len-- > 0)
              {
                  unsigned long startTime = millis();
                  while (!roomba.available())
                  {
                      // Look for a timeout
                      if (millis() > startTime + ROOMBA_READ_TIMEOUT)
                          return false; // Timed out
                  }
                  *dest++ = roomba.read();
              }
              return true;
          }
          
          //for sensorgetting
          int getPacketSize(int p) {
              return packetSizes[p - 1];
          }
          int getPacketOffset(int p) {
              int i = 0;
              for (int s = 1; s < p; s++) {
                  i += getPacketSize(s);
              }
              return i;
          }
          
          //read sensors
          void readsensors()
          {
          
              if (getSensors(6, buf, 52)) {
          
                  int off = 0;
          
                  // Battery Checks
                  off = getPacketOffset(21);
                  chargingState = buf[off + 0];
                  voltage = buf[off + 2] + 256 * buf[off + 1];
                  temp = buf[off + 5];
                  battery_Current_mAh = buf[off + 7] + 256 * buf[off + 6];
                  battery_Total_mAh = buf[off + 9] + 256 * buf[off + 8];
          
                  if (battery_Total_mAh == 0) battery_Total_mAh = 1;
                  int nBatPcent = battery_Current_mAh * 100 / battery_Total_mAh;
                  battery_percent = nBatPcent;
          
                  //Oi Mode
                  off = getPacketOffset(35);
                  OiMode = buf[off + 0];
          
                  //ChargeSource
                  off = getPacketOffset(34);
                  ChargeSource = buf[off + 0];
          
          
                  send(msg1_3.set(temp));  delay(100);
                  send(msg1_4.set(battery_percent));  delay(100);
                  send(msg1_2.set(round(voltage / 10), 1));  delay(100);
                  send(msg2_5.set(battery_Current_mAh));  delay(100);
                  send(msg1_1.set(ChargeTypes[chargingState]));  delay(100);
                  send(msg2_2.set(ChargeSource));  delay(100);
          
                  //send(msg2_5.set(chargingState));  // Send info value to gw
                  //send(msg2_5.set("charging state"));  // Send info value to gw
                  //send(msg2_5.set(battery_Total_mAh));  // Send info value to gw
                  //send(msg2_1.set(OiTypes[OiMode]));  // Send info value to gw
                  lastsensorread = millis();
          
              }
              else { send(msg1_1.set("roomba is off")); }  // Send info value to gw
          }
          
          void defsongs() {
              uint8_t zero = 0;
              roomba.write(128);  delay(100); roomba.write(131);  delay(100); //[128 131](Start the command stream and change roomba mode to "Safe" mode)
              roomba.write(140);  delay(100); roomba.write(zero); delay(100); //[140 0](Start song definition and define the first song, song number 0)
              roomba.write(25);   //[16](16 notes will be sent for song number 0)
              play('E', 4);       //[76 16](play Mi sound for 1 / 4 seconds) // E
              play('E', 4);
              play('E', 2);
              play('E', 4);
              play('E', 4);
              play('E', 2);       //[76 32](play Mi sound for 1 / 2 seconds)
              play('E', 4);
              play('G', 4);
              play('C', 4);
              play('D', 4);
              play('E', 1);
          
              play('F', 4);
              play('F', 4);
              play('F', 2);
          
              play('F', 4);
              play('E', 4);
              play('E', 2);
          
              play('E', 4);
              play('D', 4);
              play('D', 4);
              play('E', 4);
              play('D', 1);
              delay(100);
              roomba.write(128);
          }
          
          int rnote(char note) {
              int command = 31;
              switch (note) {
              case 'C': command = 72; break;
              case 'D': command = 74; break;
              case 'E': command = 76; break;
              case 'F': command = 77; break;
              case 'G': command = 79; break;
              }
              return command;
          }
          
          
          Z 1 Reply Last reply
          0
          • gohanG Offline
            gohanG Offline
            gohan
            Mod
            wrote on last edited by gohan
            #13

            Just a question, but wouldn't it be an esp8266 a more compact solution? (Not to mention the quick ota programming) Or the roomba is running 5v serial output?

            parachutesjP 1 Reply Last reply
            0
            • gohanG gohan

              Just a question, but wouldn't it be an esp8266 a more compact solution? (Not to mention the quick ota programming) Or the roomba is running 5v serial output?

              parachutesjP Offline
              parachutesjP Offline
              parachutesj
              wrote on last edited by
              #14

              @gohan ESP is probably a good alternative. but then again you could also buy an after market wireless module for your roomba.
              I am not sure about the serial output, but I think it is 5v.

              but there is plenty of space for a nano or pro mini. wemos mini or nodemcu should also fit

              1 Reply Last reply
              0
              • M Offline
                M Offline
                moskovskiy82
                wrote on last edited by
                #15

                ESP is the best solution although couldn't find the code with sensors (only clean/dock commands). I recently made a post on HomeAssistant forum - how wemos D1 fits nicely inside. You only need a buck converter and 4 cat5 wires soldered. And 5V serial output is not such a big problem as you need a voltage divider only on rx pin (didn't put it)

                P.S. But then one of those "machine revolt weeks happened" so my esproomba didn't respond and then i burned it down. Next day my RPi3 went down with HA and lots of goodies. And today ESXi...

                1 Reply Last reply
                0
                • gohanG Offline
                  gohanG Offline
                  gohan
                  Mod
                  wrote on last edited by gohan
                  #16

                  Oh man, that sucks a lot. Best luck for your disaster recovery

                  PS if everything you are touching is failing, be careful when you are going to pee 😂

                  1 Reply Last reply
                  1
                  • parachutesjP parachutesj

                    @moskovskiy82 I did not change too much compared to @EmielA on the code. worked pretty good until I bought a new roomba recently. Haven't tried it on the 980 yet:

                    // Enable debug prints to serial monitor
                    #define MY_DEBUG
                    
                    // Enable and select radio type attached
                    #define MY_RADIO_NRF24
                    //#define MY_RADIO_RFM69
                    
                    // Enable repeater functionality for this node
                    #define MY_REPEATER_FEATURE
                    
                    #define CHILD_ID_BATT_TEMP          100  // Id of the 2nd msg1_3
                    #define CHILD_ID_BATT_PROC          101   // Id of the 2nd msg1_4
                    #define CHILD_ID_BATT_VOLT          102   // Id of the 2nd msg1_2
                    #define CHILD_ID_BATT_AH            103   // Id of the 2nd msg2_5
                    #define CHILD_ID_BATT_CHARGE_TYPE   104   // Id of the 2nd msg1_1
                    #define CHILD_ID_BATT_CHARGE_SOURCE 105   // Id of the 2nd msg2_2
                    
                    
                    #include <MySensors.h>
                    #include <SoftwareSerial.h>
                    
                    //#define RELAY_1  5  // Arduino Digital I/O pin number for first relay (second on pin+1 etc)
                    //#define NUMBER_OF_RELAYS 2 // Total number of attached relays
                    //#define RELAY_ON 1  // GPIO value to write to turn on attached relay
                    //#define RELAY_OFF 0 // GPIO value to write to turn off attached relay
                    
                    #define NUMBER_OF_FUNCTION 7 
                    #define NUMBER_OF_SENSORS 6
                    #define ROOMBA_READ_TIMEOUT 300
                    #define ROOMBA_WAKE_COUNT 10
                    int ddPin = 6;
                    
                    int rxPin = 3;
                    int txPin = 4;
                    int ledPin = 13;
                    
                    SoftwareSerial roomba(rxPin, txPin);
                    
                    const char SWITCH_NAMES[NUMBER_OF_FUNCTION][14] = { "Roomba-Stop","Roomba-Dock","Roomba-Clean","Roomba-Spot","Roomba-Max","Roomba-Power","Roomba-Play" };
                    const char SENSORS_NAMES[NUMBER_OF_SENSORS][24] = { "Roomba-Batt-Temp","Roomba-Batt-Proc","Roomba-Batt-Volt", "Roomba-Batt-mAH" , "Roomba-Batt-CHt" , "Roomba-Batt-CHs" };
                    
                    
                    uint8_t buf[52];
                    boolean lowBattery = true; // Acts as a debounce
                    long battery_Current_mAh = 0;
                    long battery_Total_mAh = 0;
                    long battery_percent = 0;
                    boolean DeadBattery = false;
                    boolean FullBattery = true;
                    unsigned long chargingState = 0;
                    long voltage = 0;
                    long temp = 0;
                    long OiMode = 0;
                    long ChargeSource = 0;
                    
                    long sensorfrequency = 300000; // 300000 = 5 min - frequency the sensors are read
                    long lastsensorread = 0; // when were the sensors read the last time
                    unsigned long lastsensorssend = 0;
                    
                    const char ChargeTypes[6][34] = { "Not charging","Reconditioning Charging","Full Charging","Trickle Charging","Waiting","Charging Fault Condition" };
                    const char OiTypes[4][14] = { "Off","Passive","Safe","Full" };
                    
                    int packetSizes[58] = {
                        0,0,0,0,0,0, //1-6
                        1,1,1,1,1,1,1,1,1,1,1,1, //7-18
                        2,2, //19-20
                        1, //21
                        2,2, //22-23
                        1, //24
                        2,2,2,2,2,2,2, //25-31
                        1, //32
                        2, //33
                        1,1,1,1,1, //34-38
                        2,2,2,2,2,2, //39-44
                        1, //45
                        2,2,2,2,2,2, //46-51
                        1,1, //52-53
                        2,2,2,2, //54-57
                        1 //58
                    };
                    #define bumpright (sensorbytes[0] & 0x01)
                    #define bumpleft  (sensorbytes[0] & 0x02)
                    
                    
                    MyMessage msg1_3(CHILD_ID_BATT_TEMP, V_TEMP); //status of Temperature
                    MyMessage msg1_4(CHILD_ID_BATT_PROC, V_PH); //status of Charge [%]
                    MyMessage msg1_2(CHILD_ID_BATT_VOLT, V_VOLTAGE); //status of Voltage
                    MyMessage msg2_5(CHILD_ID_BATT_AH, V_CURRENT); //status of mAH
                    MyMessage msg1_1(CHILD_ID_BATT_CHARGE_TYPE, V_TEXT); //status of charget type
                    MyMessage msg2_2(CHILD_ID_BATT_CHARGE_SOURCE, V_TEXT); //status of charger source
                    
                    void setup() {
                        pinMode(ddPin, OUTPUT);
                        digitalWrite(ddPin, LOW);
                        Serial.begin(115200);
                        while (!Serial) {
                            ; // wait for serial port to connect. Needed for native USB port only
                        }
                        roomba.begin(115200);
                        //Serial.println("Sending start command...");
                        //delay(1000);
                        //// set up ROI to receive commands
                        defsongs();
                        delay(500);
                        rstop();
                        Serial.println("Ready to go!");
                    }
                    
                    void presentation()
                    {
                        // Send the sketch version information to the gateway and Controller
                        sendSketchInfo("Roomba", "1.2");
                    
                        for (int sensor = 0; sensor < NUMBER_OF_FUNCTION; sensor++) {
                            // Register all sensors to gw (they will be created as child devices)
                            present(sensor, S_BINARY, SWITCH_NAMES[sensor]);
                            delay(100);
                        }
                        present(CHILD_ID_BATT_TEMP, S_TEMP, SENSORS_NAMES[0]); delay(100);
                        present(CHILD_ID_BATT_PROC, S_WATER_QUALITY, SENSORS_NAMES[1]); delay(100);
                        present(CHILD_ID_BATT_VOLT, S_MULTIMETER, SENSORS_NAMES[2]);    delay(100);
                        present(CHILD_ID_BATT_AH, S_MULTIMETER, SENSORS_NAMES[3]);  delay(100);
                        present(CHILD_ID_BATT_CHARGE_TYPE, S_INFO, SENSORS_NAMES[4]); delay(100);
                        present(CHILD_ID_BATT_CHARGE_SOURCE, S_INFO, SENSORS_NAMES[5]); delay(100);
                    }
                    
                    void loop() {
                        unsigned long now = millis();
                        if (now > lastsensorssend + sensorfrequency)
                        {
                            readsensors();
                            lastsensorssend = now;
                        }
                    }
                    
                    
                    void receive(const MyMessage &message) {
                      // We only expect one type of message from controller. But we better check anyway.
                      if (message.type == V_LIGHT) {
                        int mySensor = message.sensor;
                        int myComand = message.getBool();
                    
                        //Serial.print(message.sensor-1+RELAY_1);
                    
                        if (mySensor == 0) {
                          if (myComand == 1) {
                            Serial.println("Clean");
                            //clean();
                            rcommand(135);
                          }
                          else {
                            Serial.println("Stopp1");
                            //halt();
                            rstop();
                          }
                        }
                    
                        if (mySensor == 1) {
                          if (myComand == 1) {
                            Serial.println("dock");
                            //dock();
                            rcommand(143);
                          }
                          else {
                            Serial.println("Stopp2");
                            //halt();
                            rstop();
                          }
                        }
                        if (mySensor == 2) {
                          if (myComand == 1) {
                            Serial.println("song");
                            //dock();
                            rcommand(141);
                            rsong(0);
                          }
                          else {
                            Serial.println("Stopp2");
                            //halt();
                            rstop();
                          }
                        }
                    
                      }
                    }
                    
                    void halt() {
                      ;
                      roomba.write(131);  // SAFE
                      byte j = 0x00;
                      roomba.write(137);
                      roomba.write(j);
                      roomba.write(j);
                      roomba.write(j);
                      roomba.write(j);
                    }
                    
                    void dock() {
                      delay(1000);
                      roomba.write(143);
                    }
                    
                    void clean() {
                      delay(1000);
                      roomba.write(135);
                    }
                    
                    void rstop() {
                        if (rwakeup()) {
                            roomba.write(128);  delay(100);
                            roomba.write(131);  delay(100);
                            roomba.write(137);  delay(100);
                            byte j = 0x00;
                            roomba.write(j); delay(100);
                            roomba.write(j); delay(100);
                            roomba.write(j); delay(100);
                            roomba.write(j); delay(100);
                            roomba.write(128);
                        }
                    
                    }
                    
                    void rsong(int b) {
                        roomba.write(b); delay(100);
                    }
                    
                    
                    
                    void rcommand(int command) {
                        if (rwakeup()) {
                            roomba.write(128);  delay(100);
                            roomba.write(131);  delay(100);
                            roomba.write(command);
                        }
                    }
                    
                    void play(char note, int rlong) {
                        roomba.write(rnote(note));  delay(10);  roomba.write(64 / rlong);   delay(10);
                    }
                    
                    
                    bool rwakeup() {
                        bool ret = getSensors(6, buf, 52);
                        Serial.print("wakeup: ");
                        Serial.print(ret);
                        if (!ret) {
                            for (int ii = 1; ii < ROOMBA_WAKE_COUNT; ii++)
                            {
                                for (int jj = 0; jj < 3; jj++) {
                                    digitalWrite(ddPin, HIGH); delay(10); 
                                    digitalWrite(ddPin, LOW); delay(2);
                                }
                                delay(100);
                                roomba.write(128);
                                if (getSensors(6, buf, 52)) {
                                    ret = true;
                                    break;
                                }
                                Serial.print("-");
                            }
                        }
                        Serial.println(ret);
                        return ret;
                    }
                    
                    bool getSensors(uint8_t packetID, uint8_t* dest, uint8_t len) {
                        roomba.write(142);
                        delay(100);
                        roomba.write(packetID);
                        delay(100);
                        return getData(dest, len);
                    }
                    
                    
                    bool getData(uint8_t* dest, uint8_t len)
                    {
                        while (len-- > 0)
                        {
                            unsigned long startTime = millis();
                            while (!roomba.available())
                            {
                                // Look for a timeout
                                if (millis() > startTime + ROOMBA_READ_TIMEOUT)
                                    return false; // Timed out
                            }
                            *dest++ = roomba.read();
                        }
                        return true;
                    }
                    
                    //for sensorgetting
                    int getPacketSize(int p) {
                        return packetSizes[p - 1];
                    }
                    int getPacketOffset(int p) {
                        int i = 0;
                        for (int s = 1; s < p; s++) {
                            i += getPacketSize(s);
                        }
                        return i;
                    }
                    
                    //read sensors
                    void readsensors()
                    {
                    
                        if (getSensors(6, buf, 52)) {
                    
                            int off = 0;
                    
                            // Battery Checks
                            off = getPacketOffset(21);
                            chargingState = buf[off + 0];
                            voltage = buf[off + 2] + 256 * buf[off + 1];
                            temp = buf[off + 5];
                            battery_Current_mAh = buf[off + 7] + 256 * buf[off + 6];
                            battery_Total_mAh = buf[off + 9] + 256 * buf[off + 8];
                    
                            if (battery_Total_mAh == 0) battery_Total_mAh = 1;
                            int nBatPcent = battery_Current_mAh * 100 / battery_Total_mAh;
                            battery_percent = nBatPcent;
                    
                            //Oi Mode
                            off = getPacketOffset(35);
                            OiMode = buf[off + 0];
                    
                            //ChargeSource
                            off = getPacketOffset(34);
                            ChargeSource = buf[off + 0];
                    
                    
                            send(msg1_3.set(temp));  delay(100);
                            send(msg1_4.set(battery_percent));  delay(100);
                            send(msg1_2.set(round(voltage / 10), 1));  delay(100);
                            send(msg2_5.set(battery_Current_mAh));  delay(100);
                            send(msg1_1.set(ChargeTypes[chargingState]));  delay(100);
                            send(msg2_2.set(ChargeSource));  delay(100);
                    
                            //send(msg2_5.set(chargingState));  // Send info value to gw
                            //send(msg2_5.set("charging state"));  // Send info value to gw
                            //send(msg2_5.set(battery_Total_mAh));  // Send info value to gw
                            //send(msg2_1.set(OiTypes[OiMode]));  // Send info value to gw
                            lastsensorread = millis();
                    
                        }
                        else { send(msg1_1.set("roomba is off")); }  // Send info value to gw
                    }
                    
                    void defsongs() {
                        uint8_t zero = 0;
                        roomba.write(128);  delay(100); roomba.write(131);  delay(100); //[128 131](Start the command stream and change roomba mode to "Safe" mode)
                        roomba.write(140);  delay(100); roomba.write(zero); delay(100); //[140 0](Start song definition and define the first song, song number 0)
                        roomba.write(25);   //[16](16 notes will be sent for song number 0)
                        play('E', 4);       //[76 16](play Mi sound for 1 / 4 seconds) // E
                        play('E', 4);
                        play('E', 2);
                        play('E', 4);
                        play('E', 4);
                        play('E', 2);       //[76 32](play Mi sound for 1 / 2 seconds)
                        play('E', 4);
                        play('G', 4);
                        play('C', 4);
                        play('D', 4);
                        play('E', 1);
                    
                        play('F', 4);
                        play('F', 4);
                        play('F', 2);
                    
                        play('F', 4);
                        play('E', 4);
                        play('E', 2);
                    
                        play('E', 4);
                        play('D', 4);
                        play('D', 4);
                        play('E', 4);
                        play('D', 1);
                        delay(100);
                        roomba.write(128);
                    }
                    
                    int rnote(char note) {
                        int command = 31;
                        switch (note) {
                        case 'C': command = 72; break;
                        case 'D': command = 74; break;
                        case 'E': command = 76; break;
                        case 'F': command = 77; break;
                        case 'G': command = 79; break;
                        }
                        return command;
                    }
                    
                    
                    Z Offline
                    Z Offline
                    zen85
                    wrote on last edited by zen85
                    #17
                    // Enable debug prints to serial monitor
                    #define MY_DEBUG
                    
                    // Enable and select radio type attached
                    #define MY_RADIO_NRF24
                    //#define MY_RADIO_RFM69
                    
                    // Enable repeater functionality for this node
                    #define MY_REPEATER_FEATURE
                    
                    #define CHILD_ID_BATT_TEMP          100  // Id of the 2nd msg1_3
                    #define CHILD_ID_BATT_PROC          101   // Id of the 2nd msg1_4
                    #define CHILD_ID_BATT_VOLT          102   // Id of the 2nd msg1_2
                    #define CHILD_ID_BATT_AH            103   // Id of the 2nd msg2_5
                    #define CHILD_ID_BATT_CHARGE_TYPE   104   // Id of the 2nd msg1_1
                    #define CHILD_ID_BATT_CHARGE_SOURCE 105   // Id of the 2nd msg2_2
                    
                    
                    #include <MySensors.h>
                    #include <SoftwareSerial.h>
                    
                    //#define RELAY_1  5  // Arduino Digital I/O pin number for first relay (second on pin+1 etc)
                    //#define NUMBER_OF_RELAYS 2 // Total number of attached relays
                    //#define RELAY_ON 1  // GPIO value to write to turn on attached relay
                    //#define RELAY_OFF 0 // GPIO value to write to turn off attached relay
                    
                    #define NUMBER_OF_FUNCTION 7 
                    #define NUMBER_OF_SENSORS 6
                    #define ROOMBA_READ_TIMEOUT 300
                    #define ROOMBA_WAKE_COUNT 10
                    int ddPin = 6;
                    
                    int rxPin = 3;
                    int txPin = 4;
                    int ledPin = 13;
                    
                    SoftwareSerial roomba(rxPin, txPin);
                    
                    const char SWITCH_NAMES[NUMBER_OF_FUNCTION][14] = { "Roomba-Stop","Roomba-Dock","Roomba-Clean","Roomba-Spot","Roomba-Max","Roomba-Power","Roomba-Play" };
                    const char SENSORS_NAMES[NUMBER_OF_SENSORS][24] = { "Roomba-Batt-Temp","Roomba-Batt-Proc","Roomba-Batt-Volt", "Roomba-Batt-mAH" , "Roomba-Batt-CHt" , "Roomba-Batt-CHs" };
                    
                    
                    uint8_t buf[52];
                    boolean lowBattery = true; // Acts as a debounce
                    long battery_Current_mAh = 0;
                    long battery_Total_mAh = 0;
                    long battery_percent = 0;
                    boolean DeadBattery = false;
                    boolean FullBattery = true;
                    unsigned long chargingState = 0;
                    long voltage = 0;
                    long temp = 0;
                    long OiMode = 0;
                    long ChargeSource = 0;
                    
                    long sensorfrequency = 300000; // 300000 = 5 min - frequency the sensors are read
                    long lastsensorread = 0; // when were the sensors read the last time
                    unsigned long lastsensorssend = 0;
                    
                    const char ChargeTypes[6][34] = { "Not charging","Reconditioning Charging","Full Charging","Trickle Charging","Waiting","Charging Fault Condition" };
                    const char OiTypes[4][14] = { "Off","Passive","Safe","Full" };
                    
                    int packetSizes[58] = {
                        0,0,0,0,0,0, //1-6
                        1,1,1,1,1,1,1,1,1,1,1,1, //7-18
                        2,2, //19-20
                        1, //21
                        2,2, //22-23
                        1, //24
                        2,2,2,2,2,2,2, //25-31
                        1, //32
                        2, //33
                        1,1,1,1,1, //34-38
                        2,2,2,2,2,2, //39-44
                        1, //45
                        2,2,2,2,2,2, //46-51
                        1,1, //52-53
                        2,2,2,2, //54-57
                        1 //58
                    };
                    #define bumpright (sensorbytes[0] & 0x01)
                    #define bumpleft  (sensorbytes[0] & 0x02)
                    
                    
                    MyMessage msg1_3(CHILD_ID_BATT_TEMP, V_TEMP); //status of Temperature
                    MyMessage msg1_4(CHILD_ID_BATT_PROC, V_PH); //status of Charge [%]
                    MyMessage msg1_2(CHILD_ID_BATT_VOLT, V_VOLTAGE); //status of Voltage
                    MyMessage msg2_5(CHILD_ID_BATT_AH, V_CURRENT); //status of mAH
                    MyMessage msg1_1(CHILD_ID_BATT_CHARGE_TYPE, V_TEXT); //status of charget type
                    MyMessage msg2_2(CHILD_ID_BATT_CHARGE_SOURCE, V_TEXT); //status of charger source
                    
                    void setup() {
                        pinMode(ddPin, OUTPUT);
                        digitalWrite(ddPin, LOW);
                        Serial.begin(115200);
                        while (!Serial) {
                            ; // wait for serial port to connect. Needed for native USB port only
                        }
                        roomba.begin(115200);
                        //Serial.println("Sending start command...");
                        //delay(1000);
                        //// set up ROI to receive commands
                        defsongs();
                        delay(500);
                        rstop();
                        Serial.println("Ready to go!");
                    }
                    
                    void presentation()
                    {
                        // Send the sketch version information to the gateway and Controller
                        sendSketchInfo("Roomba", "1.2");
                    
                        for (int sensor = 0; sensor < NUMBER_OF_FUNCTION; sensor++) {
                            // Register all sensors to gw (they will be created as child devices)
                            present(sensor, S_BINARY, SWITCH_NAMES[sensor]);
                            delay(100);
                        }
                        present(CHILD_ID_BATT_TEMP, S_TEMP, SENSORS_NAMES[0]); delay(100);
                        present(CHILD_ID_BATT_PROC, S_WATER_QUALITY, SENSORS_NAMES[1]); delay(100);
                        present(CHILD_ID_BATT_VOLT, S_MULTIMETER, SENSORS_NAMES[2]);    delay(100);
                        present(CHILD_ID_BATT_AH, S_MULTIMETER, SENSORS_NAMES[3]);  delay(100);
                        present(CHILD_ID_BATT_CHARGE_TYPE, S_INFO, SENSORS_NAMES[4]); delay(100);
                        present(CHILD_ID_BATT_CHARGE_SOURCE, S_INFO, SENSORS_NAMES[5]); delay(100);
                    }
                    
                    void loop() {
                        unsigned long now = millis();
                        if (now > lastsensorssend + sensorfrequency)
                        {
                            readsensors();
                            lastsensorssend = now;
                        }
                    }
                    
                    
                    void receive(const MyMessage &message) {
                      // We only expect one type of message from controller. But we better check anyway.
                      if (message.type == V_LIGHT) {
                        int mySensor = message.sensor;
                        int myComand = message.getBool();
                    
                        //Serial.print(message.sensor-1+RELAY_1);
                    
                        if (mySensor == 0) {
                          if (myComand == 1) {
                            Serial.println("Clean");
                            //clean();
                            rcommand(135);
                          }
                          else {
                            Serial.println("Stopp1");
                            //halt();
                            rstop();
                          }
                        }
                    
                        if (mySensor == 1) {
                          if (myComand == 1) {
                            Serial.println("dock");
                            //dock();
                            rcommand(143);
                          }
                          else {
                            Serial.println("Stopp2");
                            //halt();
                            rstop();
                          }
                        }
                        if (mySensor == 2) {
                          if (myComand == 1) {
                            Serial.println("song");
                            //dock();
                            rcommand(141);
                            rsong(0);
                          }
                          else {
                            Serial.println("Stopp2");
                            //halt();
                            rstop();
                          }
                        }
                    
                      }
                    }
                    
                    void halt() {
                      ;
                      roomba.write(131);  // SAFE
                      byte j = 0x00;
                      roomba.write(137);
                      roomba.write(j);
                      roomba.write(j);
                      roomba.write(j);
                      roomba.write(j);
                    }
                    
                    void dock() {
                      delay(1000);
                      roomba.write(143);
                    }
                    
                    void clean() {
                      delay(1000);
                      roomba.write(135);
                    }
                    
                    void rstop() {
                        if (rwakeup()) {
                            roomba.write(128);  delay(100);
                            roomba.write(131);  delay(100);
                            roomba.write(137);  delay(100);
                            byte j = 0x00;
                            roomba.write(j); delay(100);
                            roomba.write(j); delay(100);
                            roomba.write(j); delay(100);
                            roomba.write(j); delay(100);
                            roomba.write(128);
                        }
                    
                    }
                    
                    void rsong(int b) {
                        roomba.write(b); delay(100);
                    }
                    
                    
                    
                    void rcommand(int command) {
                        if (rwakeup()) {
                            roomba.write(128);  delay(100);
                            roomba.write(131);  delay(100);
                            roomba.write(command);
                        }
                    }
                    
                    void play(char note, int rlong) {
                        roomba.write(rnote(note));  delay(10);  roomba.write(64 / rlong);   delay(10);
                    }
                    
                    
                    bool rwakeup() {
                        bool ret = getSensors(6, buf, 52);
                        Serial.print("wakeup: ");
                        Serial.print(ret);
                        if (!ret) {
                            for (int ii = 1; ii < ROOMBA_WAKE_COUNT; ii++)
                            {
                                for (int jj = 0; jj < 3; jj++) {
                                    digitalWrite(ddPin, HIGH); delay(10); 
                                    digitalWrite(ddPin, LOW); delay(2);
                                }
                                delay(100);
                                roomba.write(128);
                                if (getSensors(6, buf, 52)) {
                                    ret = true;
                                    break;
                                }
                                Serial.print("-");
                            }
                        }
                        Serial.println(ret);
                        return ret;
                    }
                    
                    bool getSensors(uint8_t packetID, uint8_t* dest, uint8_t len) {
                        roomba.write(142);
                        delay(100);
                        roomba.write(packetID);
                        delay(100);
                        return getData(dest, len);
                    }
                    
                    
                    bool getData(uint8_t* dest, uint8_t len)
                    {
                        while (len-- > 0)
                        {
                            unsigned long startTime = millis();
                            while (!roomba.available())
                            {
                                // Look for a timeout
                                if (millis() > startTime + ROOMBA_READ_TIMEOUT)
                                    return false; // Timed out
                            }
                            *dest++ = roomba.read();
                        }
                        return true;
                    }
                    
                    //for sensorgetting
                    int getPacketSize(int p) {
                        return packetSizes[p - 1];
                    }
                    int getPacketOffset(int p) {
                        int i = 0;
                        for (int s = 1; s < p; s++) {
                            i += getPacketSize(s);
                        }
                        return i;
                    }
                    
                    //read sensors
                    void readsensors()
                    {
                    
                        if (getSensors(6, buf, 52)) {
                    
                            int off = 0;
                    
                            // Battery Checks
                            off = getPacketOffset(21);
                            chargingState = buf[off + 0];
                            voltage = buf[off + 2] + 256 * buf[off + 1];
                            temp = buf[off + 5];
                            battery_Current_mAh = buf[off + 7] + 256 * buf[off + 6];
                            battery_Total_mAh = buf[off + 9] + 256 * buf[off + 8];
                    
                            if (battery_Total_mAh == 0) battery_Total_mAh = 1;
                            int nBatPcent = battery_Current_mAh * 100 / battery_Total_mAh;
                            battery_percent = nBatPcent;
                    
                            //Oi Mode
                            off = getPacketOffset(35);
                            OiMode = buf[off + 0];
                    
                            //ChargeSource
                            off = getPacketOffset(34);
                            ChargeSource = buf[off + 0];
                    
                    
                            send(msg1_3.set(temp));  delay(100);
                            send(msg1_4.set(battery_percent));  delay(100);
                            send(msg1_2.set(round(voltage / 10), 1));  delay(100);
                            send(msg2_5.set(battery_Current_mAh));  delay(100);
                            send(msg1_1.set(ChargeTypes[chargingState]));  delay(100);
                            send(msg2_2.set(ChargeSource));  delay(100);
                    
                            //send(msg2_5.set(chargingState));  // Send info value to gw
                            //send(msg2_5.set("charging state"));  // Send info value to gw
                            //send(msg2_5.set(battery_Total_mAh));  // Send info value to gw
                            //send(msg2_1.set(OiTypes[OiMode]));  // Send info value to gw
                            lastsensorread = millis();
                    
                        }
                        else { send(msg1_1.set("roomba is off")); }  // Send info value to gw
                    }
                    
                    void defsongs() {
                        uint8_t zero = 0;
                        roomba.write(128);  delay(100); roomba.write(131);  delay(100); //[128 131](Start the command stream and change roomba mode to "Safe" mode)
                        roomba.write(140);  delay(100); roomba.write(zero); delay(100); //[140 0](Start song definition and define the first song, song number 0)
                        roomba.write(25);   //[16](16 notes will be sent for song number 0)
                        play('E', 4);       //[76 16](play Mi sound for 1 / 4 seconds) // E
                        play('E', 4);
                        play('E', 2);
                        play('E', 4);
                        play('E', 4);
                        play('E', 2);       //[76 32](play Mi sound for 1 / 2 seconds)
                        play('E', 4);
                        play('G', 4);
                        play('C', 4);
                        play('D', 4);
                        play('E', 1);
                    
                        play('F', 4);
                        play('F', 4);
                        play('F', 2);
                    
                        play('F', 4);
                        play('E', 4);
                        play('E', 2);
                    
                        play('E', 4);
                        play('D', 4);
                        play('D', 4);
                        play('E', 4);
                        play('D', 1);
                        delay(100);
                        roomba.write(128);
                    }
                    
                    int rnote(char note) {
                        int command = 31;
                        switch (note) {
                        case 'C': command = 72; break;
                        case 'D': command = 74; break;
                        case 'E': command = 76; break;
                        case 'F': command = 77; break;
                        case 'G': command = 79; break;
                        }
                        return command;
                    }
                    

                    i love this project. i am the owner of a roomba 620 and hooked it up to a arduino-mega. putting "clean()" in the code does what it should and the radiomodules are working fine. but it seems that i dont get values from the sensors and so nothing is showing up in homeassistant... :(

                    i checked the wiring about 5 times already and since starting for example "clean()" works i think it is a different issue...

                    did anyone else try this and ran into the same problem?

                    1 Reply Last reply
                    0
                    • B Offline
                      B Offline
                      banda banda
                      Banned
                      wrote on last edited by mfalkvidd
                      #18

                      there is plenty of space for a nano or pro mini. wemos mini or nodemcu should also fit
                      (spam links removed by moderator)

                      1 Reply Last reply
                      0
                      Reply
                      • Reply as topic
                      Log in to reply
                      • Oldest to Newest
                      • Newest to Oldest
                      • Most Votes


                      12

                      Online

                      11.7k

                      Users

                      11.2k

                      Topics

                      113.1k

                      Posts


                      Copyright 2025 TBD   |   Forum Guidelines   |   Privacy Policy   |   Terms of Service
                      • Login

                      • Don't have an account? Register

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • MySensors
                      • OpenHardware.io
                      • Categories
                      • Recent
                      • Tags
                      • Popular