Wiring the mess

Writing the Arduino code

Posted by Tal Akerman on December 26, 2022 · 13 mins read

Ok stay with me, if you come this far you may want to stick to the end.
It’s time to wire everything to the Arduino and write the code.

The plan - a Serial listener which will call to different functions based on the command.
The output - always a JSON text, this way it will be easier to parse on the Pi side.
The commands (which maybe change in the future):

  • initSensors - initiate the sensor’s connections.
  • getSensors - the main and most important function - get data from all the sensors
  • cameraHeaterOn/Off - turns on or off the Relay for the camera heater
  • sensorsHeaterOn/Off - turns on or off the Relay for the rain sensor
  • focuserOn/Off - turns on or off the Relay for the focuser, this is to avoid power draw from the motor
  • Focuser-Direction-Amount - “Direction” can be CW or CCW, and “Amount” can be small,med,high,max and it will move the focuser based on these parameters
  • calibrateAirSensor - calibrating the air sensors, we’ll talk about it
  • reset - reset the Arduino in case something isn’t working as expected

The wiring is pretty much straight forward…kidding, not really, this is how it will probably look like in the begining:

A mess of wires, but that’s fine… that’s fine… don’t worry I got you. Here is how it should look like :

Make yourself comfortable and try not to blow anything up.
Alternatively, if you’re lazy you can fast forward to my post about the PCB making.

And as always, for those who say - “Oh noo, tHiS coDE iS nOt eFfiCieNt” - try adding the following line to the code, it will help you, for sure:

#define TRUE FALSE

Are you still here? Good. You didn’t blow yourself or got tangled by the evil wires.
Now that we have everything wired up we can load the code to the Arduino, you can download or clone it from my Github repository

The libraries you need are are listed here

Most of the libraries can be downloaded from the IDE, but for the TSL2591 SQM reading you will need the .cpp and .h files which should be at the same location as your .ino file (Arduino code) while compling it.

#include <Arduino.h>
#include <Wire.h>
#include "Adafruit_SHT31.h"
#include <OneWire.h>
#include <DallasTemperature.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP280.h>
#include "SQM_TSL2591.h"
#include <Adafruit_MLX90614.h>
#include <FreqMeasure.h>
#include <Math.h>
#include <AccelStepper.h>
#include <Servo.h>

Or you can stay here and learn a bit about what’s what; I prefer that.

First thing first, this program takes the Nano to the limit so using F() while printing string is essential; It basically means that you can move constant strings to the program memory instead of the ram.
The program is looping for eternity, waiting for a serial connection (i.e a command) at Baud 9600, if the command is one of the predefined strings I listed in the beginning of this post, than it will run a function and return a JSON string that we will latet parse on the Pi.

For the braves - Click here to dive into the code
String initSensors()

This is a function that will run always at the setup() phase of the Arduino, or by request from the Serial connection, and will initiate/define all the sensors.
If it fails to connect to one of the sensors, it will return an error message that will be converted to JSON string and returned to the Pi.


void loop() {
  if (Serial.available() > 0) {
    String command = Serial.readStringUntil('#');

As mentioned earlier, the program is looping for eternity, waiting for a serial connection, and if find one - it will read it until the character “#”, this is a common practice in Arduino coding and Serial connection.


void getSensors()

The main function, the gold, the one that really returns what we need - the sensors readings.
It also uses external libraries that made my life a lot more easier, and the rabbit hole less deeper.


String getRainSensor(int range)
int rainSensorReading = analogRead(A0);
int range = map(rainSensorReading, rainSensorMin, rainSensorMax, 0, 3);

String getRainSensor(int range){
  switch (range) {
  case 0:    // Sensor getting very wet
      return "Raining Hard";
      break;
  case 1:    // Sensor getting wet
      return "Raining";
      break;
  case 2:    // Sensor dry
      return "Not Raining";
      break;
    }
}

The rain sensor is a cool gadget, it can be stupid and use a digital pin and return 1 (for wet) and 0 (not wet) or it can use an analog pin and return a range between 0 and 1024 (0 - very dry, and 1024 - you probably need a boat).
So what we’re doing here is to get an analog read, map the values from 0-1024 to 0-2, and deduct from it how “wet” the sensor is.
Tip: there is a potentiometer on the sensor’s board - this is to adjust the sensitivity of it, sprinkle a bit of water on the sensor (NOT THE BOARD) and see what value you are getting, adjust the dial accordingly.


sqm.takeReading()

This function uses an external library for the TSL2591 sensor, that was modified for SQM calculations. The .cpp and .h files should be at the same location as your .ino file (Arduino code) while compling it.
The library will output a few double variables - full spectrum, IR light, visible light, the SQM calculation and the uncertainty in percetage. I also added the current gain and timing configuration of the sensor reading which is dynamic based on the light conditions.


String calculateBortel(double sqm)

Will calculate the Bortle based on the SQM.


float getFreqMeasure()

Using the FreqMeasure library to calculate the light frequency from the TSL237 sensor, and convert it to SQM with Frequency to magnitudes/arcSecond2 formula.


float MQCalibration(int mq_pin)
float MQResistanceCalculation(int raw_adc)
float MQRead(int mq_pin)
long MQGetGasPercentage(float rs_ro_ratio, int gas_id)
long MQGetPercentage(float rs_ro_ratio, float *pcurve)

These functions are based on this nice article and will get us readings from the air quality sensor - in particular the precentage of Carbon Monoxide, LPG and smoke, along with the general air quality reading. The sensor calibration can take time (up to 30 seconds), so we are will not enable it for now just to save time, but it is still there as one of the Serial commands.


void heaterControl(int pin,String onoff)

Gets a ping number for a heater that is conected to the relay, and turning it on or off.


void focuserControl(bool onoff)

Turning the focuser relay pin on or off.
This design was made to save power while we don’t need to use the focuser.
Also, the focuser can get hot while on standby, so it’s better to leave it off.


void rotateMotor(int steps,String direction)

Gets number of steps, and direction - CW or CCW, and rotates the motor.


Load the code into the Arduino and test it with the IDE Serial connection:
Once the Arduino finished loading up, you should see

{'ok':'1'}###

That means that you wired everything nice and tight, or if not, you should get something like:

  
{'ok':'0','error':'ERROR:Could not find a valid SHT31b sensor, check wiring!'}###

Check your wiring, connections and solders.

Now you can send commands to the program, remember, only commands from the list will work, and they must end with “#”, for example, if you write getSensors# , you should get:

  
{'sht31a_temp':'23.34','sht31a_humidity':'26.13','sht31b_temp':'23.75','sht31b_humidity':'25.07','ds18b20_temp':'-127.00','bmp_temp':'23.54','bmp_pressure':'957.62','bmp_altitude':'468.92','mlx_ambient':'22.97','mlx_object':'22.59','tsl237_sqm':'13.36','tsl2591_full':'2197','tsl2591_ir':'572','tsl2591_visible':'1625','tsl2591_sqm':'11.14','tsl2591_uncertainty':'0.03','tsl2591_gain':'32','tsl2591_time':'1','isRaning':'Raining Hard','Bortel':'B.8/9','airSensor':'32','lpg':'0','co':'0','smoke':'0','camera_heater':'0','sensors_heater':'0','focuser':'0','ok':'1'}###

Hurray!
The Arduino side is working!
Next steps are to send and log all this data to the Pi, keep following!

~Tal