Introduction: Rotellino
Rotellino is a DIY bike light.
Rotellino works using the variation of the magnetic field, generated by the movement of the bike wheel rim, to rotate a rotor made up of 6 magnets and generate electricity.
Once working, the goal is to monitor the voltage and electricity data arriving from the sensors mounted on the rotellino with an app (blynk) on a mobile phone
Supplies
- 2 plastic bearings 623 3x10x4mm
- 6 N52 magnets 6x3 mm
- 2 M3 20 mm screws + nuts
- 2 x 5mm bright white LEDs
- Voltage and electricity sensors
- phone and google sheets for data monitoring
- 3d printer
- Drill
- Copper wire
Step 1:
build a coil by wrapping around a support the copper wire
Step 2:
attach the leds to the coil
Attachments
Step 3:
design the rotellino’s body and supports on a 3d software
Step 4:
3d print the component designed
Step 5:
assemble the rotor, made out of six magnets that are placed alternating their polarity
Step 6:
assemble all the parts together
Step 7:
write the arduino code to make the voltage and electricity sensors work and to send the data they collect to a google sheets file
Step 8:
test the functioning and of the rotellino with an oscilloscope
Step 9:
create a google sheets file to collect and analize the data took from the rotellino
Step 10:
connect arduino with google sheets
Step 11:
monitor from remote the data coming from the rotellino
Step 12: Video of the Rotellino Working
Step 13: Google Sheets Link:
https://docs.google.com/spreadsheets/d/1g_iSECpZtVPYQIcYqwiuH6AiCyx23LEwCrdgco5Elpg/edit#gid=0
Step 14: Arduino Code
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiManager.h>
#include <Adafruit_Sensor.h>
#include <time.h>
#include <ESP_Google_Sheet_Client.h>
//For ACS712 current sensor
const int sensorIn = 4;
int mVperAmp = 185; // this the 5A version of the ACS712 -use 100 for 20A Module and 66 for 30A Module
float Voltage, VRMS, AmpsRMS;
//For Hall effect sensor
int hall_pin = 17;
float hall_thresh = 5.0;
float rpm_val, time_passed;
//For Wi-fi connection
unsigned long previousMillis = 0;
unsigned long interval = 30000;
// Google Project ID
#define PROJECT_ID "iot-rotellino"
// Service Account's client email
#define CLIENT_EMAIL "iot-rotellino@iot-rotellino.iam.gserviceaccount.com"
// Service Account's private key
const char PRIVATE_KEY[] PROGMEM = "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDRU3XmSDL8oaiE\nOOu0YUdlCcqMf2UAPj0yh0roocl6VIp/bX+qKD3MHV9fOfHfxTMlcvsEGPtMDR2J\n3Kn7z2DX0s9t5D85qL3yq0FtlZarhrkt/EVFtSGyQ4brbVfRfX3z8XpTSzoA8S7L\nh++wW79ylEl+GlBP7taeSOtXDUOfEorXsu2mOv1pL7L5VJdWcGofwJJuWpjLFnWW\ndhxwPMxchRsCIAQs7ujSuoCkPVDZXXMqTE0OgbZv9qzuZ93uWn47lhuItL5XkO7D\nOFTK/vc1M6MvuXKf7riwxUp96iFcP1M7C6tsgVBoAMRr+sFTCUp+eDvkpV2vnGXO\nck5RkoBxAgMBAAECggEAB2jCzkxDmGphjFbLqlrjWf5A5UrEan/0JAaNXIErSuTq\nKrzXh+zVfyMgkz6WijBp4v0REH5X4BjeFIP6SlCFU4OsVgphszHwrexpVry0sPd3\n34VuWg093kL0LzhXMZWVfsh52QU+6qCe1HbksxUwuhh3tNAE2WPmqBESkv8uzmVF\n65rDIW5xdbe4Nt31QyhBo3FLomvGSIq6RtcTDiJGB/jtkRP3Ugs7P1Ps0wlpTcux\ngWAzNjDIkpy4dzpXsFK02qCI8QibFQia67C967EQPGJmoTUIwLQPZKaHan1d7Dpe\neAoEol+8jI859piPsE4/ZuvxseYFuo3Sa0xcxwAhdwKBgQD37QvDMZEIR5V6uSJY\nIWIWS9w2il28T+zqS8cBqVVRrJD2AL1VLAuFLMgw4vGw0MFAjufeystmpWSNSzN3\nA/awCnAyq9tivDZZTJALsIM+3llXJwxgYhAtlkGwwE72eQnJmEFGmmYqvYKbExL6\nfDvbI00GtFsDTtc7aCXa5ro09wKBgQDYJJuL0bZMiqZMlwqSOHZ15nbIhsUPEFDr\nRW8T7I4h4IvgXK9LqWfGRWg5fdDssPScPI2+eFdZpjGgEb5JAjhqSYsYIAokRw5x\nHF9oBfbuOWo6E5bso3fpeSYHKirO5MVfrehUrW0e+Z0Ylhoyg5/CuBUsHq7IgDK/\nfHY4Gk/j1wKBgHpjKktRTKcpr0DF445d7G3VRQAnjd5IFkwS3EqVrOiEp4rJEq3Y\n8FbtpGV9opIGe1/DK/NvaLljLCAT33QBIOYGQRzCeapj/vBWO0WJ/UArwy6iuBlc\nT2AxrHv0cwZ4+bvqzU5tKcIviynCYLwGWAX1hzCoF8WqRdWttAI7o/BBAoGAKWPp\nPX8tT78FVYlfBt01IiK+AGx+dAIF3Ofw+3nDRg1/+7kEAJMyQi+sY8YKKilAzmJy\nKlVVNN+0hRigvc5lC0WGE1qfVo8c3uA2DO+Hd9sa0oBJ2Ir9PYJrm9ehVvlMKqRc\n50pGqTXXtYuY/K9j+p/Rvh8qDU8vaKfm45t2TQUCgYEAwxL3f2g9V2tdyIQXN/Bj\nX9lnQtUGBvXMo6D4vqKOVejEOjfK2DVvuVJEGtbDmwJ9ljQGklybSJ19ohEujdpV\n84NVY9LiZGya9F6SaZadOPKqAvYgFwmWRfOsdiBIXlJ9FXodcd4oE4q1AFlX3Ogq\n+YyMY1xY4spSn2RxLJc8AfI=\n-----END PRIVATE KEY-----\n";
// The ID of the spreadsheet where you'll publish the data
const char spreadsheetId[] = "1g_iSECpZtVPYQIcYqwiuH6AiCyx23LEwCrdgco5Elpg";
// Timer variables
unsigned long lastTime = 0;
unsigned long timerDelay = 3000;
// Token Callback function
void tokenStatusCallback(TokenInfo info);
// NTP server to request epoch time
const char* ntpServer = "pool.ntp.org";
// Variable to save current epoch time
unsigned long epochTime;
// Function that gets current epoch time
unsigned long getTime() {
time_t now;
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
//Serial.println("Failed to obtain time");
return (0);
}
time(&now);
return now;
}
void setup() {
Serial.begin(115200);
configTime(0, 0, ntpServer); //Configure time
GSheet.printf("ESP Google Sheet Client v%s\n\n", ESP_GOOGLE_SHEET_CLIENT_VERSION);
//Wi-fi connection
WiFi.mode(WIFI_STA);
WiFiManager wm;
bool res;
// res = wm.autoConnect(); // auto generated AP name from chipid
res = wm.autoConnect("AlternatoreIOT"); // anonymous ap
//res = wm.autoConnect("AutoConnectAP", "password"); // password protected ap
pinMode(LED_BUILTIN, OUTPUT);
if (!res) {
Serial.println("Failed to connect");
// ESP.restart();
}
else {
//if you get here you have connected to the WiFi
Serial.println("connected...yeey :)");
digitalWrite(LED_BUILTIN, HIGH);
delay(1000);
digitalWrite(LED_BUILTIN, LOW);
delay(1000);
digitalWrite(LED_BUILTIN, HIGH);
delay(1000);
digitalWrite(LED_BUILTIN, LOW);
}
//Hall sensor
pinMode(hall_pin, INPUT);
//ACS712 sensor
pinMode(sensorIn, INPUT);
// Set the callback for Google API access token generation status (for debug only)
GSheet.setTokenCallback(tokenStatusCallback);
// Set the seconds to refresh the auth token before expire (60 to 3540, default is 300 seconds)
GSheet.setPrerefreshSeconds(10 * 60);
// Begin the access token generation for Google API authentication
GSheet.begin(CLIENT_EMAIL, PROJECT_ID, PRIVATE_KEY);
}
void loop() {
//Wi-fi connection
unsigned long currentMillis = millis();
// if WiFi is down, try reconnecting every CHECK_WIFI_TIME seconds
if ((WiFi.status() != WL_CONNECTED) && (currentMillis - previousMillis >= interval)) {
Serial.print(millis());
Serial.println("Reconnecting to WiFi...");
WiFi.disconnect();
WiFi.reconnect();
previousMillis = currentMillis;
}
else if (WiFi.status() == WL_CONNECTED) {
digitalWrite(LED_BUILTIN, HIGH);
}
else {
digitalWrite(LED_BUILTIN, LOW);
}
// Call ready() repeatedly in loop for authentication checking and processing
bool ready = GSheet.ready();
if (ready && millis() - lastTime > timerDelay) {
lastTime = millis();
FirebaseJson response;
Serial.println("\nAppend spreadsheet values...");
Serial.println("----------------------------");
FirebaseJson valueRange;
//Hall sensor
float hall_count = 1.0;
float start = micros();
bool on_state = false;
// counting number of times the hall sensor is tripped
// but without double counting during the same trip
while (true) {
if (digitalRead(hall_pin) == 0) {
if (on_state == false) {
on_state = true;
hall_count += 1.0;
}
}
else {
on_state = false;
}
if (hall_count >= hall_thresh) {
break;
}
}
float end_time = micros();
time_passed = ((end_time - start) / 1000000.0);
rpm_val = (hall_count / time_passed) * 60.0;
epochTime = getTime();
//ACS712
Voltage = getVPP();
VRMS = (Voltage / 2.0) * 0.707; //root 2 is 0.707
AmpsRMS = ((VRMS * 1000) / mVperAmp) - 0.3; //0.3 is the error I got for my sensor
delay (100);
valueRange.add("majorDimension", "COLUMNS");
valueRange.set("values/[0]/[0]", epochTime);
valueRange.set("values/[1]/[0]", rpm_val);
valueRange.set("values/[2]/[0]", time_passed);
valueRange.set("values/[3]/[0]", AmpsRMS);
valueRange.set("values/[4]/[0]", VRMS);
// For Google Sheet API ref doc, go to https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/append
// Append values to the spreadsheet
bool success = GSheet.values.append(&response /* returned response */, spreadsheetId /* spreadsheet Id to append */, "Foglio1!A2" /* range to append */, &valueRange /* data range to append */);
if (success) {
response.toString(Serial, true);
valueRange.clear();
}
else {
Serial.println(GSheet.errorReason());
}
Serial.println();
Serial.println(ESP.getFreeHeap());
}
}
float getVPP() {
float result;
int readValue; // value read from the sensor
int maxValue = 0; // store max value here
int minValue = 4096; // store min value here ESP32 ADC resolution
uint32_t start_time = millis();
while ((millis() - start_time) < 1000) //sample for 1 Sec
{
readValue = analogRead(sensorIn);
// see if you have a new maxValue
if (readValue > maxValue)
{
/*record the maximum sensor value*/
maxValue = readValue;
}
if (readValue < minValue)
{
/*record the minimum sensor value*/
minValue = readValue;
}
}
result = ((maxValue - minValue) * 3.3) / 4096.0; // Subtract min from max - ESP32 ADC resolution 4096
return result;
}
void tokenStatusCallback(TokenInfo info) {
if (info.status == token_status_error) {
GSheet.printf("Token info: type = %s, status = %s\n", GSheet.getTokenType(info).c_str(), GSheet.getTokenStatus(info).c_str());
GSheet.printf("Token error: %s\n", GSheet.getTokenError(info).c_str());
}
else {
GSheet.printf("Token info: type = %s, status = %s\n", GSheet.getTokenType(info).c_str(), GSheet.getTokenStatus(info).c_str());
}
}