Thanks Element14 and Tariq Ahmad for sending out Nano BLE Sense Board and a bunch of cool stickers.


In this project build, I have tried to add a PT(PushTo) support (utilizing the onboard Sensors of BLESense board) for Celestron Astromaster 130AZ, that I prefer for (Star-gazing) pastime. Moreover, the scope... interfaces with Stellarium(OpenSource Planetarium Software) that renders realistic projection of the night-sky in real time. Do check out the web-version.

This project has been deeply inspired from StellarScope

Basics of astronomy... (Euclidean Arithmetics, Quaternion's rotation and BRs..)

In astronomy, a celestial coordinate system (or celestial reference system) is a system for specifying positions of satellites, stars, planets, galaxies and other celestial objects. Coordinate systems can specify an object's position in three-dimensional space or plot merely its direction on a celestial sphere, if the object's distance is unknown or trivial. The coordinate systems are implemented in either spherical or rectangular coordinates. Spherical coordinates, projected on the celestial sphere, are analogous to the geographic coordinate system used on the surface of Earth. These differ in their choice of fundamental plane, which divides the celestial sphere into two equal hemispheres along a great circle. Rectangular coordinates, in appropriate units, are simply the cartesian equivalent of the spherical coordinates, with the same fundamental (x, y) plane and primary (x-axis) direction. Each coordinate system is named after its choice of fundamental plane.

Horizontal system

The horizontal, or altitude-azimuth, system is based on the position of the observer on Earth, which revolves around its own axis once per sidereal day (23 hours, 56 minutes and 4.091 seconds) in relation to the star background. The positioning of a celestial object by the horizontal system varies with time, but is a useful coordinate system for locating and tracking objects for observers on Earth. It is based on the position of stars relative to an observer's ideal horizon.

Equatorial system

This coordinate system is centered at Earth's center, but fixed relative to the celestial poles and the March equinox. The coordinates are based on the location of stars relative to Earth's equator if it were projected out to an infinite distance. The equatorial describes the sky as seen from the Solar System, and modern star maps almost exclusively use equatorial coordinates. The equatorial system is the normal coordinate system for most professional and many amateur astronomers having an equatorial mount that follows the movement of the sky during the night. Celestial objects are found by adjusting the telescope's or other instrument's scales so that they match the equatorial coordinates of the selected object to observe.

Popular choices of pole and equator are the older B1950 and the modern J2000 systems, but a pole and equator "of date" can also be used, meaning one appropriate to the date under consideration, such as when a measurement of the position of a planet or spacecraft is made. There are also subdivisions into "mean of date" coordinates, which average out or ignore nutation, and "true of date," which include nutation.

The Euler angles are three angles that are used to describe the orientation of a rigid body with respect to a fixed coordinate system. They can also represent the orientation of a mobile frame of reference in physics or the orientation of a general basis in 3-dimensional linear algebra. Euler angles can be defined by elemental geometry or by composition of rotations. The geometrical definition demonstrates that three composed elemental rotations (rotations about the axes of a coordinate system) are always sufficient to reach any target frame.


Sidereal time is a timekeeping system that astronomers use to locate celestial objects. Using sidereal time, it is possible to easily point a telescope to the proper coordinates in the night sky. Briefly, sidereal time is a "time scale that is based on Earth's rate of rotation measured relative to the fixed stars". Viewed from the same location, a star seen at one position in the sky will be seen at the same position on another night at the same sidereal time. This is similar to how the time kept by a sundial can be used to find the location of the Sun. Just as the Sun and Moon appear to rise in the east and set in the west due to the rotation of Earth, so do the stars. Both solar time and sidereal time make use of the regularity of Earth's rotation about its polar axis, solar time following the Sun while sidereal time roughly follows the stars. More exactly, sidereal time is the angle, measured along the celestial equator, from the observer's meridian to the great circle that passes through the March equinox and both celestial poles, and is usually expressed in hours, minutes, and seconds. Common time on a typical clock measures a slightly longer cycle, accounting not only for Earth's axial rotation but also for Earth's orbit around the Sun.

A sidereal day is approximately 23 hours, 56 minutes, 4.0905 seconds (24 hours − 4 minutes + 4.0905 seconds = 86164.0905 s = 23.9344696 h). (Seconds here follow the SI definition and are not to be confused with ephemeris second.) The March equinox itself precesses slowly westward relative to the fixed stars, completing one revolution in about 26,000 years, so the misnamed sidereal day ("sidereal" is derived from the Latin sidus meaning "star") is 0.0084 seconds shorter than the stellar day, Earth's period of rotation relative to the fixed stars. The slightly longer "true" sidereal period is measured as the Earth Rotation Angle (ERA), formerly the stellar angle. An increase of 360° in the ERA is a full rotation of the Earth.

NanoSky Code(getting the star-gazing_altitude and environmental parameters into your smartphone):

#include <Arduino_HTS221.h>
#include <ArduinoBLE.h>
#include <Arduino_LPS22HB.h>
float Po = 1013.25; // average pressure at mean sea-level (MSL) in the International Standard Atmosphere (ISA) is 1013.25 hPa
float P, T;
float To = 273.15;
float k = 0.0065;

BLEService sensorService("181A");  // Environmental_Sensor-Service
BLEShortCharacteristic sensorLevelChar1("2A6E", BLERead | BLENotify);   // Temperature-Characteristic
BLEShortCharacteristic sensorLevelChar2("2A6F", BLERead | BLENotify);   // Humidity-Characteristic
BLELongCharacteristic sensorLevelChar3("2A6C", BLERead | BLENotify);    // Elevation-Characteristic

int oldSensorLevel1 = 0; 
int oldSensorLevel2 = 0;
int oldSensorLevel3 = 0;
long previousMillis = 0; 

void setup() {
  while (!Serial);
  if (!HTS.begin()) {
    while (1);
  if (!BARO.begin()) {
    while (1);
  pinMode(22, OUTPUT);
  pinMode(23, OUTPUT);
  if (!BLE.begin()) {
    Serial.println("Bluetooth init. failed!");
    while (1);

  // start advertising
  Serial.println("Bluetooth active, waiting to be connected.....");

void loop() {
  // waiting for a BLE central device.
  BLEDevice central = BLE.central();
  if (central) {
    Serial.print("Connected to central: ");
    digitalWrite(22, LOW);  // Reverse-logic; //turns LED ON
    digitalWrite(23, LOW);  // indicate successful connection
    while (central.connected()) {
      long currentMillis = millis();
      if (currentMillis - previousMillis >= 200) {
        previousMillis = currentMillis;
    digitalWrite(22, HIGH);   // turns R_LED OFF
    digitalWrite(23, HIGH);   // turns G_LED OFF
    Serial.print("Disconnected from central: ");

void updateSensorLevel1() {
  float temperature = HTS.readTemperature();
  Serial.print("Temperature = ");
  Serial.print(temperature, 0);
  T = temperature;
  Serial.println(" °C");
  float sensorLevel1 = map(temperature, 0, 100, 0, 10000);
  oldSensorLevel1 = sensorLevel1;

void updateSensorLevel2() {
  float humidity    = HTS.readHumidity();
  Serial.print("Humidity = ");
  Serial.print(humidity, 0);
  Serial.println(" %");
  float sensorLevel2 = map(humidity, 0, 100, 0, 10000);
  oldSensorLevel2 = sensorLevel2; 

void updateSensorLevel3() {
  float pressure = BARO.readPressure();
  P = pressure*10;  // in hectoPascals. // [1hPa = 10kPa]
  float fP = (Po/P);       // pressure-factor.
  float x = (1/5.257);     // power-constant.
  double z = pow(fP,x);    // exponential pressure-grad.
  float height = (((z-1)*(T+To)) / k); 
  Serial.print("Altitude = ");
  Serial.println(" metres");
  float sensorLevel3 = map(height, 0, 100, 0, 10000);
  oldSensorLevel3 = sensorLevel3; 


NightSky Code:

#include <Arduino_LSM9DS1.h>
#define declination +0.36 // Lucknow, Kanpur // User location-specific.
// Geographical latitude (example: North 26º55'35'')
int latHH = 26;    // 26º North
int latMM = 55;
int latSS = 35;
int Altitude = 56;  //  Barometric and GPS(external)compensated(metres)
// Pole Star RA(right ascension J2000.0)// (AR: HH:MM:SS) // update on 2050
int poleAR_HH = 2;  
int poleAR_MM = 31;
int poleAR_SS = 51;
// Pole Star RA(right ascension on-date)// (AR: HH:MM:SS)
//int poleAR_HH = 2;   
//int poleAR_MM = 58;
//int poleAR_SS = 7;
// Pole Star HA(hour angle)// (H: HH:MM:SS)
int poleH_HH = 9;
int poleH_MM = 34;
int poleH_SS = 9;
char txAR[10];
char txDEC[11];
char input[20];
long LST;   // Lateral Sideral Time
unsigned long tsideral = 930;  // 15.30'
unsigned long t_cycle_aq = 0, t_cycle;
long Az_tel_s, Alt_tel_s;
long AR_tel_s, DEC_tel_s;
long AR_stell_s, DEC_stell_s;
double cosine_phi, sine_phi;
double alt, azi;

void setup()
  cosine_phi = cos((((latHH * 3600) + (latMM * 60) + latSS) / 3600.0) * PI / 180.0);
  sine_phi = sin((((latHH * 3600) + (latMM * 60) + latSS) / 3600.0) * PI / 180.0);
  LST = poleAR_HH * 3600 + poleAR_MM * 60 + poleAR_SS + poleH_HH * 3600 + poleH_MM * 60 + poleH_SS;
  while (LST >= 86400) LST = LST - 86400;

void loop()
  t_cycle = millis();
  if (t_cycle_aq >= tsideral) {
    t_cycle_aq = t_cycle_aq - tsideral;
    if (LST >= 86400) {
      LST = LST - 86400;
  double delta_tel, sin_h, cos_h, sin_A, cos_A, sin_DEC, cos_DEC;
  double H_telRAD, h_telRAD, A_telRAD;
  long H_tel;
  long arHH, arMM, arSS;
  long decDEG, decMM, decSS;
  char sDEC_tel;
  A_telRAD = (Az_tel_s / 3600.0) * PI / 180.0;
  h_telRAD = (Alt_tel_s / 3600.0) * PI / 180.0;
  sin_h = sin(h_telRAD);
  cos_h = cos(h_telRAD);
  sin_A = sin(A_telRAD);
  cos_A = cos(A_telRAD);
  delta_tel = asin((sine_phi * sin_h) + (cosine_phi * cos_h * cos_A));
  sin_DEC = sin(delta_tel);
  cos_DEC = cos(delta_tel);
  DEC_tel_s = long((delta_tel * 180.0 / PI) * 3600.0); 
  while (DEC_tel_s >= 648000) {
    DEC_tel_s = DEC_tel_s - 648000;  // EXP5/24
  while (DEC_tel_s <= -648000) {
    DEC_tel_s = DEC_tel_s + 648000;
  H_telRAD = acos((sin_h - (sine_phi * sin_DEC)) / (cosine_phi * cos_DEC));
  H_tel = long((H_telRAD * 180.0 / PI) * 240.0);
  if (sin_A >= 0) {
    H_tel = 86400 - H_tel;
  AR_tel_s = LST - H_tel;
  while (AR_tel_s >= 86400) {
    AR_tel_s = AR_tel_s - 86400;
  while (AR_tel_s < 0) {
    AR_tel_s = AR_tel_s + 86400;
  arHH = AR_tel_s / 3600;
  arMM = (AR_tel_s - arHH * 3600) / 60;
  arSS = (AR_tel_s - arHH * 3600) - arMM * 60;
  decDEG = abs(DEC_tel_s) / 3600;
  decMM = (abs(DEC_tel_s) - decDEG * 3600) / 60;
  decSS = (abs(DEC_tel_s) - decDEG * 3600) - decMM * 60;
  (DEC_tel_s < 0) ? sDEC_tel = 45 : sDEC_tel = 43;
  sprintf(txAR, "%02d:%02d:%02d#", int(arHH), int(arMM), int(arSS));
  sprintf(txDEC, "%c%02d%c%02d:%02d#", sDEC_tel, int(decDEG), 223, int(decMM), int(decSS));
  if (Serial.available() > 0) LX200();
  t_cycle = millis() - t_cycle;
  t_cycle_aq = t_cycle_aq + t_cycle;
  analogWrite(22, random(0, 20));
  analogWrite(23, random(0, 45));
  analogWrite(24, random(0, 150));

void LX200()
  int i = 0;
  input[i++] =;
  while ((input[i++] = != '#') {
  input[i] = '\0';
  if (input[1] == ':' && input[2] == 'G' && input[3] == 'R' && input[4] == '#') {
  if (input[1] == ':' && input[2] == 'G' && input[3] == 'D' && input[4] == '#') {

void aq_IMU() {
  float roll, pitch, yaw;
  float X, Y, Z, l, m, n;
  if ( (IMU.accelerationAvailable()) && (IMU.magneticFieldAvailable()) ){
    IMU.readAcceleration(X, Y, Z);
    IMU.readMagneticField(l, m, n);
    roll = atan2(Y, Z);
    //pitch = atan2(-X, sqrt(Y * Y + Z * Z));
  if (m == 0)
    yaw = (l < 0) ? PI : 0;
    yaw = atan2(l, m);
    yaw -= declination * PI / 180;
  if (yaw > PI) yaw += (PI);
  else if (yaw > -PI) yaw += (PI);
  // Converting Radians to Degrees:
  roll  *= 180.0 / PI;
  //pitch *= 180.0 / PI;
  yaw *= 180.0 / PI;
  float r = (roll);
  float y  = (yaw);
  Alt_tel_s = 2000*r; // can be pitch(depends on mounting orientation)
  Az_tel_s  = 2000*y;
  if (Az_tel_s < 0) Az_tel_s = 1296000 + Az_tel_s;
  if (Az_tel_s >= 1296000) Az_tel_s = Az_tel_s - 1296000; // EXP15/24


While, minor sensor drifts were observed while getting the raw roll & pitch values. The ideal spot to mount Nano was found to be the ABS rotary block on tri-pod frontend. Hence, slightly avoiding (not completely) the ferrous distortion-effect on Magnetometer readings.

Interfacing with Stellarium:

After compiling and uploading the code, get ready to connect your scope to Stellarium through Meade-LX200 Serial Protocol.

Punch in your GPS co-ords. and Elevation(can be obtained from on-board barometer; available on BLESense33), familiarize yourself with available controls and features, hence after create and configure the telescope plugin with the board Serial Port. Click on connect. Wait and searchout your Scope-marker in the Galactic-View ..may the Force be with you(to find little green men).

                                                        Vega (Contact).. in progress


Resources & Learning:

Local Sidereal Time Clock

Astronomy by JavaScript: Sun Calculators and More




Bad Bot 2.0

Thanks(again) Tariq and Element14 for also sending out the Element14 Bot(passive automata).

Recently, I picked upon some of the quite interesting blogs posted under 'Restoration&Repair', and a few of them inspired myself for (unwillingly) performing un-official teardown of the Bot. Here is what I found;

Training to become a three babysteps;

1) Adding an Arduino ProMicro as a (backpack),

2) Wire-in red and green LED and

3) Uploading a rogue(BadUSB) script.

                        Double wielded lightsaber effect [T: PongKrell ; B: DarthMaul]

The below arduino code executes a local batch file (video), or can directly open a specific URL (code), based on HID class. For instance, I have hard-coded the following links for safe execution on (users) default web browser as well has the capability to launch specific links in different browsers.

- Rise of the Jedi [Rey vs Ren] streamed on youtube.

- Project14 homepage.

- Element14 community webpage.


(execution in random-order)

Bad Bot(2.0) Code:

#include <Keyboard.h> 
void setup()
  pinMode(9, OUTPUT);     // Red_LED::during payload delivery
  pinMode(10, OUTPUT);    // Green_LED::payload deployment
  // Begining the stream
  //digitalWrite(9, HIGH);
  //digitalWrite(9, LOW);
  // Waiting 500ms for init
  // Running on Linux;;
  // Running on MacOS;' ');
  // Though running on Windows, the input language changes;(Key:Press;;3 times);' ');
  delay(200);;' ');
  // On another OS, this can result "   r". // Performing Backspace 4 times.
  for(int i = 0; i < 4; i++) {
  // Get in the URL, I chose Project14.
  Keyboard.print(""); // can be any of the ducky-scripts.
  //Keyboard.print("Def.bat"); //Pre-payload prep.
  digitalWrite(10, HIGH);

void typeKey(int key)

void loop() {}


Official Rubber-Ducky Payloads[Hak5]:

Official WebOpenerCode[URL@atime]:

REM Linux run dialog
REM Mac OS run dialog
REM On Windows this changes the input language, so press 3 times
REM On another OS, this could have typed " r". Backspace 4 times.
REM Type in URL and open page!