AIM
To keep doctors and nurses attending to corona victims safe and reduce their workload
OPPORTUNITY
Given the rate at which we are losing our frontline warriors - our medical staff, who are being exposed to the dangerous Coronavirus during treatment, it is important for us to ensure their safety. Already, there is a lack of medical equipment and staff and hence a lot of stress on health workers. On top of that are indisciplined patients and the highly communicable nature of the virus. Most hospitals are understaffed and to cater to many patients at once is difficult. This is where the system I propose comes in.I wish to make a ground wheeled bot to attend to and monitor patients based on the Arduino MKR1010 Wifi and Arduino IoT Cloud.
IDEA
To attend to corona patients for even trivial tasks like providing them medicine or checking their temperature, doctors and nurses put themselves at great risk to attend to them. To prevent this, I propose a ground robot capable of locomotion inside a hospital environment and attending to patients and fulfilling some basic needs and even raising an alarm in case of an emergency. Each bed would be given a pre-assigned identification ID. The instructions to the bot would be sent to it wirelessly over Wi-Fi(if the hospital has a connection) by the doctors or nurses through an app that connects to a web application based on Arduino IoT Cloud, from the comfort of their homes.
Through an Arduino IoT Events script. Instructions like checking the body temperature, giving medicines and essentials would be given through the app. The main controller for the bot would be based on the Arduino MKR1010 Wifi which would control the four-wheeled bot.
Also, the patient may summon the bot at any time, through a calling button on the bedside. As soon as the bot receives a signal to attend to a patient, it goes to the specific bed and wakes up the IR LED on the bedside with the help of the magnet attached on it, that sends a coded message corresponding to that bed. If the codes match, then the bot proceeds with further actions.
This setup was chosen purposely to ensure data security which is a critical issue these days. Also, the wakeup mechanism ensures that the MKR board which is capable of drawing less power during sleep mode may not drain the battery too quickly. A panel of buttons would also be provided on the bedside, through which the patient may ask for some essentials like water or call for medical staff. If the doctor wants to see the patient, the bot may also give audio and video footage with relevant peripherals on the MKR board. The instructions to the patient would be given through the Display shield on the board. The logged temperature data would be used to control the windows in the ward and the air-conditioning.
Also, the module mounted on the patient`s bed may also trigger emergency messages for the doctors and help in case of an emergency, taking sensor data from ventilators or other monitoring equipment. In such an emergency, if the attending doctor is not available on-site, he may initiate certain basic tasks through the external plug and play modules that can be mounted on the bot for things like fluid injections, or tweaking settings of life-support modules like ventilators till competent staff can attend.
The application would keep track of all willing, active and competent doctors on the platform, who would be issued a notification if the doctor attending to that particular patient is not available(something like Ola and Uber). Enrollment would be on a voluntary and competency basis. This would help reduce the workload on doctors at hospitals where the number of patients is large and allow doctors in those areas that are not affected much to pitch in and do their bit.
The doctor can monitor all patients` conditions through the app that interfaces with AWS IoT Core, including ECG and other critical sensor data, which could help in giving life-saving instructions to hospital staff remotely. The data of each patient would be stored in the cloud which can then be shared in medical and academic circles to use it for drug discovery and development of better medical practices.
The app would have the doctor control over the camera module with which monitoring indisciplined patients can also be done, without risking physical proximity. The platform can as well be extended to attend to patients with other problems as well.
BENEFITS
- Safety of doctors and other staff
- Reducing theworkload of health workers
- Life-saving treatment remotely at the right time
- Remote monitoring of patients
Mechanical Design
The bot would be a four-wheeled differential drive. The arm would be fitted with the camera module and the place at the back can be used to mount a variety of modules for different kinds of medical work.
The modules would be plug and play and can be controlled by the doctors remotely. The material would be ABS plastic. Most of the chassis would be made with cutouts of generic plastic sheets and boxes, reducing manufacturing time and cost.
USP
- Optimal implementation of social distancing for doctors
- IoT connected entities(bots, beds, equipment and application)
- - Remote access to patient and medical equipment
- - Low-cost design
- Easy to prototype and implement(suitable in remote environments)
- Patient monitoring on-the-go
- Notifications about patient condition
- Unified platform of doctors to reduce the workload
- Prevents physical harm and indiscipline towards doctors
- Datalogging on the cloud to help with future treatments
- Power-efficient
- Portable and scalable
As I have mentioned before, I have partnered with a COVID hospital nearby to make an autonomous delivery robot for them that can among other things - clean the ward, sanitise it, carry medicines, enable audio-video communication between doctor and patient and raise alarms in case of an emergency. It is linked with a web application made on Arduino IoT Cloud that helps one visualise the data that is gathered. I will explain how I solved each of these design challenges one by one.
ArchitectureThese are the various modules that have been included in the design. Understanding it clearly upfront is important because of the documentation following may confuse you.
This is the distribution of power and data lines in the robot. It clearly outlines the signal flow
These are the various inputs and outputs of the robot(essentially the MKR board)
The IoT ConnectIn this diagram, I have tried to show explicitly what data is monitored and how it is sent where
Now, I will explain each subsystem.
Locomotion Mechanism
Probably the most challenging part was making the locomotion module. The first thing I did was getting some aluminium C channels and cutting them into 1m strips which I joined in a square formation. I secured the ends with hot glue and bolted them together. Then, I bolted L-shaped steel channels onto the square frame and fitted the high torque Johnson motors(60rpm). I also hooked up 2A motor drivers and corresponding circuitry to the motors. The control lines were routed to a controller board I made for the MKR on a 20X30 zero board.
I hooked up a 8000mAh LiPo battery pack to the power lines and a FLysky RF transceiver to wirelessly control it. Configuring the Flysky transceiver kit can easily be understood from this tutorial :
Taking a leap of faith, I slowed raised the throttle. The bot moved and I was delighted. My fancy ended in despair as I found that the high torque motor had put so much shearing stress on the C-channel that it collapsed.
I understood that make-do solutions won't do and consulted a friend to get the mounts 3D-printed. This was some task given that I had absolutely no experience on the mechanical side.
We made samples, broke some of them and re-iterated. This is what we managed in the end.
As you might have already noticed, I had to add another 4 motors for changing the direction.
Then, the LIDAR and the line sensor arrays were added after we confirmed that the movement was acceptable. This was a very painful task as tuning control parameters was not that easy. It took me three days to get it working properly. Here is the final video:
Here I have removed the tray on which essentials are carried and the robotic arm that delivers medicines so that the viewer can appreciate the autonomous traversal algorithm
https://drive.google.com/file/d/17Cbb-IQMIuLTcGxveTo5NY_0y1xGPGul/view?usp=sharing
Once, the locomotion mechanism was complete, I started with the communication code.
Doing this was pretty simple and just involved tweaking the example Wifi server application on the Arduino MKR1010 and configuring the ESP-12F boards to be the Wifi clients. These are pretty simple tasks even for beginners and many tutorials are already available on Youtube and Hackster.io alike.
Connecting to Arduino IoT Cloud
- Go to Arduino IoT Cloud
Make an account and verify your email address(not helping you with that)
- Make an account and verify your email address(not helping you with that)
- Go to the Devices tab and connect your Arduino MKR1010 Wifi to your computer via a microUSB-B cable
- On successful detection, you should see this
Board found
- Give a name to your device(No spaces or special characters)
- Create a new Thing
- Give the thing a name
- Go to the Add Property tab and add all the necessary data configuration
- Your device is all set to send data to the cloud
- Edit the automatically generated Sketch and upload to your board using Arduino Create Agent which you will be prompted to download for your preferred browser
- Go to your Dashboards tab and view the incoming data
Connecting with Google Sheets for visualisation and easy cross-platform access
In the Web Editor we need to add some code to the template sketch that has been generated. First, before the setup function, we define three constants for the board pins that will be used:
#define PIN_TEMP A1
#define PIN_HUM A2
#define PIN_BATT 5
#define PIN_TEMP A1
#define PIN_HUM A2
#define PIN_BATT 5
Next, in the setup function, we initialize pin modes:
pinMode(PIN_TEMP, INPUT);
pinMode(PIN_HUM, INPUT);
pinMode(PIN_BATT, INTPUT);
pinMode(PIN_TEMP, INPUT);
pinMode(PIN_HUM, INPUT);
pinMode(PIN_BATT, INTPUT);
In the loop function we read analog values from the sensors:
int wd_temp = (analogRead(PIN_TEMP) * 3.3) / 1024.0 - 0.5) * 100.0;
int wd_hum = analogRead(PIN_HUM)*100*RHF;
float batt_percent = analogRead(PIN_BATT)/4.2 * constB;
int wd_temp = (analogRead(PIN_TEMP) * 3.3) / 1024.0 - 0.5) * 100.0;
int wd_hum = analogRead(PIN_HUM)*100*RHF;
float batt_percent = analogRead(PIN_BATT)/4.2 * constB;
Now we must fill the Secret Tab with our WiFi credentials and then upload the sketch to our Arduino board. When the sketch is uploaded, the board should connect to the cloud (we can check by opening the Serial Monitor) and we should see values changing every 30s in the dashboard view of IoT Cloud.
Google Apps Script
Google Apps ScriptNow let’s put aside the Arduino part of this project and let’s concentrate on the Google app.
First thing first, go to Google Sheets and create a new blank spreadsheet. Rename the sheet to something like “RoboData”, then click on Tools > Script Editor to open Google Apps Script.
- First thing first, go to Google Sheets and create a new blank spreadsheet. Rename the sheet to something like “RoboData”, then click on Tools > Script Editor to open Google Apps Script.
- Go to Tools > Script Editor to open Google Apps Script
- In the window that appears we can write some code that will be executed whenever a message is sent from the IoT Cloud using webhooks. Our goal is to read incoming data from the cloud and write them in a spreadsheet as a table. We want to write not only the properties’ values but also the date and time at which they are updated.
- First, we define variables for our spreadsheet and for the sheet which will contain numerical data:
var sheet = SpreadsheetApp.getActiveSheet().getSheetByName('RoboData');
var sheet = SpreadsheetApp.getActiveSheet().getSheetByName('RoboData');
Here we define some constants:
var MAX_ROWS = 1440; // max number of data rows to display
var HEADER_ROW = 1; // row index of header
var TIMESTAMP_COL = 1; // column index of the timestamp column
var MAX_ROWS = 1440; // max number of data rows to display
var HEADER_ROW = 1; // row index of header
var TIMESTAMP_COL = 1; // column index of the timestamp column
- We define MAX_ROWS because we want to maintain constant the number of rows of our table, i.e. each time an extra row is written, we delete the last one. In order to define MAX_ROWS we have chosen a total buffer time of 12 hours, then considering that the Cloud updates properties every 30 seconds, we can calculate MAX_ROWS = 3600s / 30s * 12h = 1440.
The function doPost(e) {} receives post request from the IoT Cloud and stores the incoming data into the variable e. Inside this function, we will automatically update our spreadsheet.
- In order to get sensible data from e, we have to interpret it as a JSON object:
var cloudData = JSON.parse(e.postData.contents);
var cloudData = JSON.parse(e.postData.contents);
cloudData is now a JSON object with the following structure:
{webhook_id: 312064b8-ea32-5711-959b-6643b8ce8d2c,
device_id: 268a4d5e-d882-4961-9fff-abc42b641142,
thing_id: d161b2f0-a830-4c01-b0d7-6cdf0d9f6456,
values: [{id: 1552695e-50b8-4cd3-b2c7-826bdeedb0ec,
name: power_switch,
value: true,
persist: false,
updated_at: 2019-02-25T09:59:52.14Z,
created_by: f5d24652-25f8-4295-abce-d056ac47b3d8},...,{}]}
{webhook_id: 312064b8-ea32-5711-959b-6643b8ce8d2c,
device_id: 268a4d5e-d882-4961-9fff-abc42b641142,
thing_id: d161b2f0-a830-4c01-b0d7-6cdf0d9f6456,
values: [{id: 1552695e-50b8-4cd3-b2c7-826bdeedb0ec,
name: power_switch,
value: true,
persist: false,
updated_at: 2019-02-25T09:59:52.14Z,
created_by: f5d24652-25f8-4295-abce-d056ac47b3d8},...,{}]}
Notice that the values field is itself an array of JSON objects!
- We can extract parameters from cloudData in the following way:
var webhook_id = cloudData.webhook_id;
var device_id = cloudData.device_id;
var thing_id = cloudData.thing_id;
var values = cloudData.values;
var webhook_id = cloudData.webhook_id;
var device_id = cloudData.device_id;
var thing_id = cloudData.thing_id;
var values = cloudData.values;
- Next, we store names and values in separate arrays just to make things more manageable:
var incLength = values.length;
var incNames = [];
var incValues = [];
for (var i = 0; i < incLength; i++) {
incNames[i] = values[i].name;
}
var incLength = values.length;
var incNames = [];
var incValues = [];
for (var i = 0; i < incLength; i++) {
incNames[i] = values[i].name;
}
- Now we read the timestamp of the incoming message and we turn it into a javascript date object. Since the timestamp is already formatted as yyyy-MM-ddTHH:mm:ss.mmmZ, we can use the Date.parse() method:
var timestamp = values[0].updated_at;
var date = new Date(Date.parse(timestamp));
var timestamp = values[0].updated_at;
var date = new Date(Date.parse(timestamp));
- Due to inconsistency and presence of duplicate messages, we add some checks so to accept messages in ascending order with respect to time:
if (date.getYear() > 2018) {
// discard all messages that arrive 'late'
if (sheet.getRange(HEADER_ROW+1, 1).getValue() != '') { // for the first time app is run
var now = new Date();
var COMM_TIME = 5; // rough overestimate of communication time between cloud and app
if (now.getTime() - date.getTime() > COMM_TIME * 1000) {
return;
}
}
}
if (date.getYear() > 2018) {
// discard all messages that arrive 'late'
if (sheet.getRange(HEADER_ROW+1, 1).getValue() != '') { // for the first time app is run
var now = new Date();
var COMM_TIME = 5; // rough overestimate of communication time between cloud and app
if (now.getTime() - date.getTime() > COMM_TIME * 1000) {
return;
}
}
}
Important note: adjust COMM_TIME based on the WiFi connection quality of your board! Moreover, COMM_TIME must be smaller than the update interval chosen in the IoT Cloud (30s in our case).
- If the incoming message gets through previous checks, we can start writing data into our spreadsheet.
- The following code block automatically detects properties’ names and writes them in the header row.
sheet.getRange(HEADER_ROW, 1).setValue('timestamp');
for (var i = 0; i < incLength; i++) {
var lastCol = sheet.getLastColumn();
if (lastCol == 1) {
sheet.getRange(HEADER_ROW, lastCol + 1).setValue(incNames[i]);
} else {
// check if the name is already in header
var found = 0;
for (var col = 2; col <= lastCol; col++) {
if (sheet.getRange(HEADER_ROW, col).getValue() == incNames[i]) {
found = 1;
break;
}
}
if (found == 0) {
sheet.getRange(HEADER_ROW, lastCol+1).setValue(incNames[i]);
}
}
}
sheet.getRange(HEADER_ROW, 1).setValue('timestamp');
for (var i = 0; i < incLength; i++) {
var lastCol = sheet.getLastColumn();
if (lastCol == 1) {
sheet.getRange(HEADER_ROW, lastCol + 1).setValue(incNames[i]);
} else {
// check if the name is already in header
var found = 0;
for (var col = 2; col <= lastCol; col++) {
if (sheet.getRange(HEADER_ROW, col).getValue() == incNames[i]) {
found = 1;
break;
}
}
if (found == 0) {
sheet.getRange(HEADER_ROW, lastCol+1).setValue(incNames[i]);
}
}
}
- Next, we delete the last row, if the maximum number of rows has been reached:
var lastCol = sheet.getLastColumn();
var lastRow = sheet.getLastRow();
if (lastRow > MAX_ROWS + HEADER_ROW - 1) {
sheet.deleteRow(lastRow);
}
var lastCol = sheet.getLastColumn();
var lastRow = sheet.getLastRow();
if (lastRow > MAX_ROWS + HEADER_ROW - 1) {
sheet.deleteRow(lastRow);
}
- We insert a new row, specifying its style:
sheet.insertRowAfter(HEADER_ROW);
var range = sheet.getRange('A2:Z2');
range.setFontColor('#000000');
range.setFontSize(10);
range.setFontWeight('normal');
sheet.insertRowAfter(HEADER_ROW);
var range = sheet.getRange('A2:Z2');
range.setFontColor('#000000');
range.setFontSize(10);
range.setFontWeight('normal');
- And finally, we write the timestamp and all other values:
sheet.getRange(HEADER_ROW+1, TIMESTAMP_COL).setValue(date).setNumberFormat("yyyy-MM-dd HH:mm:ss");
// first copy previous values
// this is to avoid empty cells if not all properties are updated at the same time
sheet.getRange(HEADER_ROW+1, col).setValue(sheet.getRange(HEADER_ROW+2, col).getValue());
for (var i = 0; i < incLength; i++) {
var currentName = sheet.getRange(HEADER_ROW, col).getValue();
if (currentName == incNames[i]) {
// turn boolean values into 0/1, otherwise google sheets interprets them as labels in the graph
if (incValues[i] == true) {
incValues[i] = 1;
} else if (incValues[i] == false) {
incValues[i] = 0;
}
sheet.getRange(HEADER_ROW+1, col).setValue(incValues[i]);
}
}
}
sheet.getRange(HEADER_ROW+1, TIMESTAMP_COL).setValue(date).setNumberFormat("yyyy-MM-dd HH:mm:ss");
// first copy previous values
// this is to avoid empty cells if not all properties are updated at the same time
sheet.getRange(HEADER_ROW+1, col).setValue(sheet.getRange(HEADER_ROW+2, col).getValue());
for (var i = 0; i < incLength; i++) {
var currentName = sheet.getRange(HEADER_ROW, col).getValue();
if (currentName == incNames[i]) {
// turn boolean values into 0/1, otherwise google sheets interprets them as labels in the graph
if (incValues[i] == true) {
incValues[i] = 1;
} else if (incValues[i] == false) {
incValues[i] = 0;
}
sheet.getRange(HEADER_ROW+1, col).setValue(incValues[i]);
}
}
}
- In order to actually use this code we first need to publish it, thus go to Publish > Deploy as web app. If you’ve never saved the project before you will be asked to name it. In the popup window select a new project version and make sure to give access to the app to “anyone, even anonymous”, then click on deploy.
Select New Project Version and give access to anyone
Copy the web app URL that appears and click OK.
Copy the web app URL
The copied URL will be our webhook the IoT Cloud will use to access the Google app. We must copy this URL in the IoT Cloud edit view under webhook and click Update.
Paste the URL under webhook in the IoT Cloud
At this point, let’s power our circuit! If everything is working correctly, after the board is connected to the cloud we should see rows appearing in our spreadsheet once every 30 seconds. Remember that there is a bit of delay between the cloud and the spreadsheet updates, so don’t worry if the timestamps don’t match up perfectly with the actual time: they should have a delay of some seconds, depending on the quality of the WiFi connection.
Making Sense of the Data - with Charts
The only thing that remains to do is to create cool charts and add some style to the spreadsheet. For example, we can make the header text bold, change its color and set alternating colours for other rows. We can also choose specific date and number formats for each column by going to Format > Number.
In order to add a chart, select the timestamp column and another column (you need to select the entire column, not a limited range), then click on the chart icon, and a new chart will be created. To avoid problems due to scrolling of the sheets when a new row is added, click on the three dots on the top right of the chart window and click on Move to own sheet. This will move the chart into a new sheet and from there we can customize it as we want by clicking the Edit button.
Cleaning Mechanism
The mechanism is nothing new. I placed a servo head on top of a sprayer bottle handle that pushed down and sprayed sanitiser onto the floor and the sides of the robot. I got this sprayer bottle on amazon which I filled with 1L of water and placed a TowerPro 9g servo motor on top with the help of mounting screws(M3 size). The servo was controlled by the chief microcontroller on board, ie, the MKR
I could not estimate the amount of mist sprays but I estimate anywhere around a 100 sprays with around 50ml still left in the bottle.
The Disinfection Mechanism-my favourite
The Disinfection Mechanism-my favouriteUltraviolet light destroys the genetic material in pathogens—DNA in bacteria and fungi, RNA in viruses—preventing them from reproducing.
This is the 11W lamp that I used for the disinfector. It was fixed vertically on the central vertical axis of the bot and mounted from the base on an MG995 servo motor. I also attached an Aluminium sheet in the form of a quadrant behind the bulb for better reflection. In the process, I used up all the aluminium foil at home - Don`t tell my mother xD!!
Powering this thing up was a challenge, as it drew a lot of power and had to be operated from an AC supply. I first thought of making my own invertor but I found that making a sine wave invertor would take time. SO I ordered this simple AC to DC convertor that is able to supply enough power for my UV C Lamp.
Calculations for the UV lamp rating requirement
While selecting a UVC lamp one has to be really careful as just selecting any random UVC lamp would not suffice a particular. Also, medical guidelines need to be met while
Final Circuit
I must say that this project has turned out to be the most economically taxing project for me. However, I must say that I have enjoyed throughout. I ended up spending a mammoth 300$. This is how I spent the amount:
https://docs.google.com/spreadsheets/d/1xxzlfJThkISSSLX-Ai6zzygMf7h-JJnN6emyjOt12Qk/edit#gid=0
Fortunately, I did receive some funding from the Tata Main hospital I was working with and I am happy that my project is being used to do something worthwile unlike other fun projects.
Future Work
Future work includes getting my bot certified for use in healthcare facilities. I cannot rely on Arduino Cloud forever and the goal is to shift to a more configurable webservice. An android app was supposed to be made but the hardware testing took so much time that I could not develop that. I will make sure to get it done in the next couple of days.
It is very unfortunate that I cannot post the video of the actual working of the robot in an actual COVID ward because of agreements with hospital management. I also cannot disclose some critical code related to the traversal, security and maintenance due to these agreements. However, all the other code is enclosed herewith.
Conclusion
I must say that I have achieved exactly what the contest set me up for - to Detect and Protect with Arduino. All in all, flashy as it may seem, the cost of installing the system in a hospital with 40 beds is much lower than even the cheapest of ventilators!!!
Considering the number of doctors we will be able to save this way, I believe this technology should at least be given a trial. The cost is low and the development time very less.
However, this is just the beginning. There are so many problems waiting to be solved and the Arduino MKR Wifi 1010 opens up so many doors. I have many plans for using MKR1010 for my future projects.
Do watch out!! Till then - bye and thanks for reading.
References
https://wiki.dfrobot.com/HUSKYLENS_V1.0_SKU_SEN0305_SEN0336#target_0
http://www.tatamainhospital.com/
https://create.arduino.cc/iot/
Comments