John Shovic's Google Plus Switch: Project Curacao - Software System - Part 6

Project Curacao - Software System - Part 6

Project Curacao - The Software System




System description

Project Curacao consists of four subsystems. A Raspberry Pi Model A is the brains and the overall controller. The Power subsystem was described in part 1, the Environmental Sensor subsystem was described in part 2 and the Camera subsystem was shown in part 3.  This article goes through the software architecture of the system.

There are two computers in Project Curacao.  The main "brains" of the project reside in the Raspberry Pi in a program called ProjectCuracaoMain.  The ProjectCuracaoMain program contains approximately 5000 lines of code in Python. The BatteryWatchdog Arduino has 3000 lines of code written in C/C++.  The third  program, RasPiConnect, contains 3000 lines of code (counting only Local.py - the customized code) and is used for display, control and remote access from an iPhone or iPad.


Architecture of the Python code

The ProjectCuracaoMain Python file is not complex.  It consists of some setup code and then the initialisation of the Python package apscheduler with the various programs to be run periodically.  The scheduling code is very simple and is shown below.

scheduler = Scheduler()
 job = scheduler.add_cron_job(powerdatacollect.datacollect5minutes, minute="*/5", args=['main', 0])
 job = scheduler.add_cron_job(watchdogdatacollect.watchdogdatacollect, minute="*/5", args=['main', 10])
 job = scheduler.add_cron_job(environdatacollect.environdatacollect, minute="*/15", args=['main', 70])
 job = scheduler.add_cron_job(systemstatistics.systemstatistics15minutes, minute="*/15", args=['main',40])
 job = scheduler.add_cron_job(doallgraphs.doallgraphs, minute="*/15", args=['main',10,100])
 # camera
 job = scheduler.add_cron_job(useCamera.takeSinglePicture, hour="*", args=['main',50])
 # send daily picture
 job = scheduler.add_cron_job(sendPictureEmail.sendPictureEmail, hour="22",minute="20", args=['main',0])
 sys.stdout.write('Press Ctrl+C to exit\n')
 scheduler.start()

The key concept of the above code is that each of the jobs is fired off in its own thread and not in the main thread of ProjectCuracaoMain. What is the advantage of this?  If the thread code bombs then just the thread code stops running, leaving the main program and other threads intact. 


Project Curacao Architecture


You can also run more than one thing at a time, but we minimise that to avoid the load on the Raspberry Pi. The apscheduler Python package, detailed at  http://pythonhosted.org/APScheduler, generates the threads for you, so it is very easy to use.

We communicate between threads through files in the "state" directory.  One improvement we will be making in the thread code is to put in a lock for the BatteryWatchdog serial port to keep threads from stepping on each other when sending and receiving serial data out to the BatteryWatchdog.  New to threading?  An excellent tutorial is at  http://www.tutorialspoint.com/python/python_multithreading.htm.

Each of the threads are simple pieces of code that actuate hardware, send email, build graphs, collect data and write to the MySQL database.

The remainder of the main program code is for setup and handling interrupts.


Doing hard time

The main software (and the BatteryWatchdog) is a realtime system. It is characterized as a "soft" realtime system because it is not guaranteed that a particular task will run at the exact time requested.  Other system tasks may interrupt the software.  Tasks can even be lost. This is fine for this system, but you don't want your car braking system to operate that way. "Hard" realtime systems are designed to ensure that all tasks are executed when required.


Watchdog timer

Anyone who has written complex code knows that there are still bugs remaining in the software which could prevent the software from functioning.  It is also true that the Raspberry Pi OS sometimes hangs and there is always the danger of a voltage spike, lightning bolt or something causing the Raspberry Pi to stop responding.  We combat this with a watchdog timer implemented with an Arduino to reboot the Raspberry Pi periodically and also when there is an issue. The Raspberry Pi sends an "I am still here" to the BatteryWatchdog every five minutes (in the sendWatchDogTimer.py program under the housekeeping directory).  

If the BatteryWatchdog doesn't receive these messages for 15 minutes, then it sends an interrupt to the Raspberry Pi and tells it to shutdown (always a good idea if possible). The BatteryWatchdog  waits for 5 minutes and then power cycles the Raspberry Pi.  Then you have the question of "Who watches the watchdog?". This is partially answered by the use of a deadman switch in the BatteryWatchdog software, which was detailed last month. A better solution would be to implement a hardware timer that would reboot the BatteryWatchdog if it goes away.


Crontab entry

Crontab is a utility on the Pi that will periodically call a program or a command. We only use crontab for one thing in Project Curacao and that is to send the graphs and current photograph to a webpage hosted by our good friends at MiloCreek (developer of RasPiConnect). You can see them at  http://milocreek.com/projectcuracaographs.

Architecture of the BatteryWatchdog 
Arduino Architecture


The BatteryWatchdog is built on a state machine model. We have two main states in the system.  State0 contains housekeeping code, the timed alarms and the watchdog code.  State0 is the "normal" run state of the system.
When an interrupt is received from the Raspberry Pi the software transitions to State1, where the serial data from the Raspberry Pi is dealt with and data is sent to the Raspberry Pi. This is a classic software state machine design.  


void loop() {
  // state machine
  printStateVariables();

  switch (currentState)
  {
     case  STATE0:
       nextState = state0(currentState);
     break;
    
     case STATE1:
        nextState = state1(currentState);
     break;
     
     default: 
     break;
  }
  checkForAlarms();

  if (nextState == currentState)
  {
    // sleep
    delay(1000);
    sleep.pwrDownMode();
    sleep.sleepDelay(sleepTime); 
  }
  currentState = nextState;
}

Note that we did not use the timed alarm Arduino library as it doesn't work if you put the Arduino to sleep (to save current).  We also used an average reading technique to reduce the noise and issues in the Arduino built-in analogue-to-digital converters.


Total sensor count

We have 11 I2C devices in Project Curacao.  We also use 14 GPIO pins and a total of 5 A/D pins.  The table is below.


RaspBerry Pi i2c Mappings

i2c addressDeviceFunction
0x29BM017 / TCS34725RGBC Color Sensor
0x39TSL2561Luminosity Sensor
0x40INA219Raspberry Pi Input Voltage/Current
0x41INA219Battery Input Voltage/Current
0x44INA219Solar Panel Output Voltage/Current
0x48ADS1915 A/D12 bit A/D converter
Channel 0Pi Battery Temperature
Channel 1Camera Servo Feedback Potentiometer
0x77BMP085Baro/Temp readings

RaspBerry Pi GPIO Mappings (GPIO.setmode(GPIO.board))

RPI.GPIORaspberry Pi NameBCM2835Pin NumberFunction
7GPIO7GPIO4P1_07Rising Edge Interrupt to Arduino
8TXDGPIO14P1_08TXD to Arduino
10RXDGPIO15P1_10RXD to Arduino
12GPIO1GPIO18P1_12Software PWM for Camera Servo
15GPIO3GPIO22P1_15Pulse to Turn Fan Off
16GPIO4GPIO23P1_16DHT22 Temp/Hum Interface
18GPIO5GPIO24P1_18Pulse to Turn Fan On
22GPIO6GPIO25P1_22Activity LED



Arduino Battery Watchdog i2c Mappings



i2c addressDeviceFunction
0x40INA219Arduino Input Voltage/Current
0x41INA219Arduino Solar Cell Voltage/Current
0x44INA219Arduino Battery Voltage/Current
0x5CAMS 2315Outside Temp/Humidity
0x68RTC1307Real Time Clock

Arduino Battery Watchdog GPIO Mappings

GPIOFunctionComments
22Interrupt PiLevel
27Pi Latched Relay ResetPulse
29Pi Latched Relay SetPulse
35LED Pi Interrupt OnArduino Battery Voltage/Current
41Select Wind SetWind Turbine
43Select Solar ResetSolar Panels


Arduino Battery Watchdog A/D Mappings


ChannelFunction
A3Pi Battery Voltage
A5Regulated Wind Voltage
A7Unregulated Wind Voltage
A9Pi Solar Voltage


RasPiConnect code

The RasPiConnect app can be obtained from http://www.milocreek.com. All the code is located in the Local.py file available on GitHub https://github.com/projectcuracao/RasPiConnectServer.  It involves a total of 93 controls and a number of graphs.  We use it on a regular basis to monitor the project.


Remote access to the system

Remote access to the system is achieved with SSH and by HTTPS via RasPiConnect. Since these ports are exposed to the internet both are password protected with 12 letter random passwords.  A rule to remember is ANYTIME you expose a port to the internet, you need to use complex difficult passwords. We use https://identitysafe.norton.com/password-generator to generate these.


All of the code used in this article is posted on GitHub at https://github.com/projectcuracao.

No comments:

Post a Comment