Add wifi to an older roomba

I used a Wemos(lolin) d1 mini, and the dc shield. I will post my sketch tomorrow, along with a quick wiring diagram. Easier to program the first time since it’s USB, and then the shield handles the voltage conversion without issue.

Great. i’ll like to use a D1 Mini.

Can someone tell me what i need to buy exactly to do this project.

Thx

1 Like

I used the following

Wemos D1 mini
Wemos DC Shield
3.3v to 5v level shifter
PNP transistor (2N2907A, 2N3906, or 2N4403) wired like this https://www.irobot.com/~/media/MainSite/PDFs/About/STEM/Create/Create_2_to_5V_Logic.pdf

Hoping to have time tomorrow to draw out how it is all connected.

I couldn’t find the Wemos DC shield in Fritzing, and the pins on the mini din are not labeled right(correct number is 1-7, top to bottom), but I hope this helps

Will try this weekend to pull mine apart and take close up pics of how I stacked and soldered everything together.

2 Likes

Thanks Ecam315!’Can you share your sketch?

this is my main.cpp

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <ArduinoOTA.h>
#include <Roomba.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include "config.h"
extern "C" {
#include "user_interface.h"
}

// Remote debugging over telnet. Just run:
// telnet roomba.local
#if LOGGING
#include <RemoteDebug.h>
#define DLOG(msg, ...) if(Debug.isActive(Debug.DEBUG)){Debug.printf(msg, ##__VA_ARGS__);}
RemoteDebug Debug;
#else
#define DLOG(msg, ...)
#endif

// Roomba setup
Roomba roomba(&Serial, Roomba::Baud115200);

// Network setup
WiFiClient wifiClient;

// MQTT setup
PubSubClient mqttClient(wifiClient);
const PROGMEM char *commandTopic = MQTT_COMMAND_TOPIC;
const PROGMEM char *statusTopic = MQTT_STATE_TOPIC;

os_timer_t wakeupTimer;


void doneWakeup(void *pArg) {
  digitalWrite(BRC_PIN, HIGH);
  roomba.start();
  DLOG("Done waking up\n");
}

void wakeup(){
  // TODO: There's got to be some black magic here that I'm missing to keep the
  // Roomba from going into deep sleep while on the dock. Thinking Cleaner
  // had a software solution, so it must be possible:
  // http://www.thinkingcleaner.com/setup/problem_solutions/
  // I've tried:
  // * Pulsing the BRC pin low at various intervals
  // * Switching to safe and full modes briefly
  // Maybe I could try:
  // * Switching on a motor or led very very briefly?
  // * Changing the baud rate
  // * Setting BRC to input (high impedence) instead of high
  // I have noticed sometimes I'll get a sensor packet while the BRC pin is
  // pulsed low but this is super unreliable.
  DLOG("Wakeup Roomba\n");
  digitalWrite(BRC_PIN, LOW);
  // Spin up a timer to bring the BRC_PIN back high again
  os_timer_disarm(&wakeupTimer);
  // I tried to use a c++ lambda here, but for some reason it'd fail on the 6th
  // iteration. I wonder if something is keyed off the function pointer.
  os_timer_setfn(&wakeupTimer, doneWakeup, NULL);
  os_timer_arm(&wakeupTimer, 1000, false);
 }

bool performCommand(const char *cmdchar) {
  // TODO: do this only if necessary
  wakeup();

  // Char* string comparisons dont always work
  String cmd(cmdchar);

  // MQTT protocol commands
  if (cmd == "turn_on") {
DLOG("Turning on\n");
roomba.cover();
  } else if (cmd == "turn_off") {
DLOG("Turning off\n");
roomba.power();
  } else if (cmd == "toggle") {
DLOG("Toggling\n");
roomba.cover();
  } else if (cmd == "stop") {
DLOG("Stopping\n");
roomba.cover();
  } else if (cmd == "clean_spot") {
DLOG("Cleaning Spot\n");
roomba.spot();
  } else if (cmd == "locate") {
DLOG("Locating\n");
// TODO
  } else if (cmd == "return_to_base") {
DLOG("Returning to Base\n");
roomba.dock();
  } else {
return false;
  }
  return true;
}

void mqttCallback(char *topic, byte *payload, unsigned int length) {
  DLOG("Received mqtt callback for topic %s\n", topic);
  if (strcmp(commandTopic, topic) == 0) {
// turn payload into a null terminated string
char *cmd = (char *)malloc(length + 1);
memcpy(cmd, payload, length);
cmd[length] = 0;

if(!performCommand(cmd)) {
  DLOG("Unknown command %s\n", cmd);
}
free(cmd);
  }
}

void debugCallback() {
  String cmd = Debug.getLastCommand();

  // Debugging commands via telnet
  if (performCommand(cmd.c_str())) {
  } else if (cmd == "quit") {
DLOG("Stopping Roomba\n");
Serial.write(173);
  } else if (cmd == "rreset") {
DLOG("Resetting Roomba\n");
roomba.reset();
  } else if (cmd == "mqtthello") {
mqttClient.publish("vacuum/hello", "hello there");
  } else if (cmd == "version") {
const char compile_date[] = __DATE__ " " __TIME__;
DLOG("Compiled on: %s\n", compile_date);
  } else {
DLOG("Unknown command %s\n", cmd.c_str());
  }
}

void setup() {
  pinMode(BRC_PIN, OUTPUT);
  digitalWrite(BRC_PIN, HIGH);

  // Set Hostname.
  String hostname(HOSTNAME);
  WiFi.hostname(hostname);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  while (WiFi.status() != WL_CONNECTED) {
delay(500);
  }

  ArduinoOTA.setHostname((const char *)hostname.c_str());
  ArduinoOTA.begin();

  mqttClient.setServer(MQTT_SERVER, 1883);
  mqttClient.setCallback(mqttCallback);

  #if LOGGING
  Debug.begin((const char *)hostname.c_str());
  Debug.setResetCmdEnabled(true);
  Debug.setCallBackProjectCmds(debugCallback);
  Debug.setSerialEnabled(false);
  #endif

  roomba.start();
}

void reconnect() {
  DLOG("Attempting MQTT connection...\n");
  // Attempt to connect
  if (mqttClient.connect(HOSTNAME)) {
DLOG("MQTT connected\n");
mqttClient.subscribe(commandTopic);
  } else {
DLOG("MQTT failed rc=%d try again in 5 seconds\n", mqttClient.state());
  }
}

void sendStatus() {
  // Flush serial buffers
  while (Serial.available()) {
Serial.read();
  }

  uint8_t sensors[] = {
Roomba::SensorDistance, // 2 bytes, mm, signed
Roomba::SensorChargingState, // 1 byte
Roomba::SensorVoltage, // 2 bytes, mV, unsigned
Roomba::SensorCurrent, // 2 bytes, mA, signed
Roomba::SensorBatteryCharge, // 2 bytes, mAh, unsigned
Roomba::SensorBatteryCapacity // 2 bytes, mAh, unsigned
  };
  uint8_t values[11];

  bool success = roomba.getSensorsList(sensors, sizeof(sensors), values, 11);
  if (!success) {
DLOG("Failed to read sensor values from Roomba\n");
return;
  }
  int16_t distance = values[0] * 256 + values[1];
  uint8_t chargingState = values[2];
  uint16_t voltage = values[3] * 256 + values[4];
  int16_t current = values[5] * 256 + values[6];
  uint16_t charge = values[7] * 256 + values[8];
  uint16_t capacity = values[9] * 256 + values[10];

  DLOG("Got sensor values Distance:%dmm ChargingState:%d Voltage:%dmV Current:%dmA Charge:%dmAh Capacity:%dmAh\n", distance, chargingState, voltage, current, charge, capacity);

  bool cleaning = false;
  bool docked = false;

  String state;
  if (current < -300) {
cleaning = true;
  } else if (current > -50) {
docked = true;
  }

  StaticJsonBuffer<200> jsonBuffer;
  JsonObject& root = jsonBuffer.createObject();
  root["battery_level"] = (charge * 100)/capacity;
  root["cleaning"] = cleaning;
  root["docked"] = docked;
  root["charging"] = chargingState == Roomba::ChargeStateReconditioningCharging
  || chargingState == Roomba::ChargeStateFullChanrging
  || chargingState == Roomba::ChargeStateTrickleCharging;
  root["voltage"] = voltage;
  root["current"] = current;
  root["charge"] = charge;
  String jsonStr;
  root.printTo(jsonStr);
  mqttClient.publish(statusTopic, jsonStr.c_str());
}

int lastStateMsgTime = 0;
int lastWakeupTime = 0;

void loop() {
  long now = millis();
  // If MQTT client can't connect to broker, then reconnect
  if (!mqttClient.connected()) {
reconnect();
  } else {
if (now - lastWakeupTime > 50000) {
  lastWakeupTime = now;
  wakeup();
}
if (now - lastStateMsgTime > 1000) {
  lastStateMsgTime = now;
  sendStatus();
}
  }

  ArduinoOTA.handle();
  yield();
  Debug.handle();
  mqttClient.loop();
}
1 Like

Sorry for the long radio silence from me. I’m starting to invest in this project again! I’ve accepted the PRs that were open on the GitHub repo.

Is anyone using this successfully with a Roomba 650?

I am with the code above.

Interesting. I bet your model is one of the 650s manufactured before Feb 2015. I think that’s when the glitchy models that don’t stay awake came out

I do have to pulse the BRC pin, otherwise it does stop responding. Which means when it gets stuck in the middle of the night it reminds me constantly until I go unstick it.

That’s expected and documented in the Roomba docs. The weird thing is that on newer 650s it doesn’t actually work to keep the roomba awake.

Hey guys,

I got a Roomba 621 and I’m wondering if it is possible to use your script with a NodeMCU ESP8266.
Is there any wiring diagram out there for connecting to this board? I just find it somehow complex understanding your wiring diagrams for Wemos D1 Mini and the blank ESP8266. :blush:

1 Like

I was wondering if you can share the diagram for your ESP-01 setup.

1 Like

Hey there,

i got it working with ESP8266 NodeMCU, my Roomba 621 and this repository: https://github.com/incmve/roomba-esp8266/
Here is my wiring diagram:

I just tried the repo from johnboiles (https://github.com/johnboiles/esp-roomba-mqtt) but got no luck.
(D) Failed to read sensor values from Roomba
I tried with the Hardware-Serial-Pins (GPIO1 & GPIO3). I read that they are used only when the board is connected via usb, so dunno what pins to use with this repo (which is using the HardwareSerial from Roomba.h library).

Some little update:
The reading of sensors isn’t working with my actual setup. I would like to test it with the PNP-Transistor. Anyone knows how to wire it to a 3,3V-Input of the ESP8266?

1 Like

Yup! I been trying this code (johnboiles) in multiple ESP-01s, ESP-12Es, and NodeMCU ESP-12es without success. I guess I’m gonna give a try this one too. Thanks!

I wonder if it’s something about using the hardware serial port that’s not working. I see this repo uses SoftwareSerial. It would be interesting for someone to change my version to use Software Serial and see how it behaves.

The Roomba-Library from Mike McCauley is using the HardwareSerial. So i think u have to change the whole library!?

I really like your sleepIfNecessary() which is reading Battary-Voltage per ADC-Pin. How did you connect the ADC/A0 Pin? Unfortunateley it is not shown in your schematic :confused:

I spent some time last night hooking up a Wemos D1 Mini with a 3.3v-5v bidirectional level shifter so I would have true 5V logic levels to the Roomba. I was fairly certain this would work, and possibly even make things more reliable than my original setup (that uses 3.3v to drive the 5v UART on the Roomba). However, I could not get it to work at all. I’m at a loss as to why this would be. I was unable to send any commands to the Roomba or get it to respond at all. I used my oscilloscope to sanity check that I was sending serial commands to the Roomba and everything looked acceptable – the output of the level shifter wasn’t as clean as I would have liked it, but it was still at higher voltages than it would have been with the original 3.3v setup. There’s something weird happening that makes the Roomba’s serial port tricky to interface with, and I’m very curious what it is. Please keep posting your experiences. The more data points we have the more likely it is we can figure out what’s happening.

I also moved my code away from using query-ing for sensor values (opcode 149) and added a streaming (opcode 148) implementation. I’ll push it up to GitHub in the next couple days. The stream interface has its quirks (it will return the packets I ask it to stream, and also a bunch of other things, sometimes), but it seems to work much more consistently than the query interface.