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



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());

  }

}