Can Android mode do timelapse image capture and FTP upload

HI,
I have an upcoming project where i would like to:

  1. capture an image,
  2. store the image to on-board storage (SD card or internal),
  3. upload the image to an FTP server,
  4. wait till next image capture time then repeat.

I have successfully done this using a microprocessor (Arduino MKR) and camera module on a breakout board but i thought it might be better and more fun to do it using a cell-phone programmed using processing for Android.

Is this something that could be accomplished using Android mode in processing or would i be better off using Android studio, droid script or something similar.

Any help or ideas is much appreciated.

Cheers.

1, 2, and 4 are 100% possible. Not sure how to connect to ftp server but im sure theres some code in the wild for that.

@katesfb ===
1,2,3 are easy to do with android processing (or AS of course); as for 4 i think that you need some callback from the server

HI,
And thanks for the replies, its good to know that it all seems to be doable.

Another feature that will be required is that the phone goes into some form of power saving mode when not collecting and uploading an image, to prolong battery life.

Apparently there is a power manager class that can be accessed by importing

android.os.PowerManager

Has anybody had any experience of using the power manager class for putting the phone into a power saving mode?

Any help is much appreciated.

Cheers.

@katesfb ===
No; the PowerManager API is not for what you want; on the contrary it permits to lock the device with a wakeLock, complete or partial; as for acceeding to “power saving mode” there is not any way as far than i know because each device has specific mode to do that manually.

HI,
And thanks for the replies. Sorry for lack of response.

Well i finally have a time lapse imaging system up and running on a phone. As per initial description it captures an image, saves image to internal shared storage, uploads the image to an FTP server, then sits and waits till next scheduled capture time.

However a problem that exists is that as soon as the phone goes into standby, the time lapse operation stops so i thought a wakelock might help here and i found this post (How to use Partial WakeLock) that you guys were also involved in. Would the code examples given in this post be useful for this application or is another approach required. I quite like the idea of using a thread but surely that is also affected by the phone going into standby.

Any help is much appreciated.

Cheers.

@katesfb === in this case you can use the AlarmManager class and some pending Intent + one broadcast receiver class, which uses (automatically) partial WakeLock; the problem is that AlarmManager wants a time rather long (more than some seconds)

hi where is @noel if you know ???

Have you tried @noels code.

https://raw.githubusercontent.com/EmmanuelPil/Android-java-code-utilities-widgets-for-Processing-for-Android/master/Keep%20screen%20alive

He’s gone buddy. He decided a few months back to move on to other things.

@paulgoux
i know he is left and he left upset from something :confused: :confused: :confused: :confused: :confused:

Hi,
And thanks for the replies.

@akenation:

I had already had a look at this but it looked quite complex to set up (although would be perfect as the app is only collecting images every 15 to 20 minutes). So i decided to try the wake-lock approach first as i only need to acquire the wake-lock and then leave it as it will never get released.

However i may not understand exactly how the wakelock method actually works; It is my understanding that when a wake lock is acquired, the system will not go into standby (screen will still blank which is good) but CPU remains active so that everything else will still work e.g output to the console would work etc. However the following small test code compiled and ran but did not work as expected:

import android.os.PowerManager.WakeLock;
import android.os.Bundle;
import android.os.PowerManager;
import android.app.Activity;
import android.content.Context;

int count = 0;
PowerManager.WakeLock wakeLock;
PowerManager pm;
WakeLock wl;

void setup() 
{
  background(255, 0, 0);
  pm = (PowerManager) getActivity().getSystemService(Context.POWER_SERVICE);
  wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My wakelock");
  
  println("Requesting write permissons for wake lock");
  requestPermission("android.permission.WAKE_LOCK", "initWakeLock");
  
  acquirewakeLock();
}
    
void draw()
{  
  println(count);
  count++;
  delay(500);
  
  println(wl.isHeld());
}

void acquirewakeLock()
{
  wl.acquire();
  println("Wakelock on> "+wl.isHeld());    
}

void initWakeLock(boolean granted) {
  if (granted) {   
    println("Wake Lock access OK");
  } else {
    println("Wake Lock access not available");
  }
}

Console output is as follows:

BUILD SUCCESSFUL in 5s
25 actionable tasks: 25 executed
Requesting write permissons for wake lock
Wakelock on> true
0
Wake Lock access OK
true
1
true
2
true
3
true
4
true
5

however as soon as the phone went into standby the console output stopped. I assumed that the output would continue when the screen blanked. Is this not the case?

As a matter of interest what is the difference between using a wake-lock and simply setting the phone to never go to sleep and then blanking the screen in the code via mousePressed() or something.

Any help with this is much appreciated.

@katesfb === i dont understand why you say that alarmManager is not easy to use; i can put code for that but as you seem to code successfully i put here only something as a guide line; so:

  • in your fragment P5 you create a method for creating and starting the alarm; of course you have imported the class and the imports related to (intent, pendingIntent…)- at this point you can create an alarm which fires at some seconds- or hours or; it can fire one time or repeat. This alarm must define an intent and a pending intent; the intent is adressed to the receiver and used for constructing the pending intent.
  • then you create a java class for your receiver, extending wakefulBroadcastReceiver class; you have to import it also in your class; this class has only 1 mandatory method “onReceive” that you override defining what happens at this moment: “go and load something …” or start some service" (“send a SMS”, send a notification vibrate or start a music…) "
  • in your settings you create an instance of this class : that is your receiver for messages from the alert; you can register it dynamically defining someFilter (because all alarms triggered by applications are not useful for you) or in your Manifest but you have to register it in your application context. You have also to unregister it with onStop( )or onDestroy or onPause(); that is for battery reason.
  • and now everything is ok: if you want you can start the alert eg.with a mouseReleased event, or in your settings, but after having created the instance from your receiver.

try and tell me; though i work i can find the time to help, if needed.

HI,
And thanks for your reply and your help with this, much appreciated.

My programming skills are really not that good. My back ground is Arduino based and python mainly, I have done some stuff using the processing IDE but as soon as i have to do anything that involves proper Java like broadcast receivers or intents etc or even classes i turn pale and fall over.

Anyway i will go through what you have set out as a guide and have a crack at it. The android developer site is also quite useful and if i can get it to work, i think it will be quite useful going forward as i have other projects that could make good use of it.

I will let you know how i get on.

Cheers.

@katesfb === ok! - if you need help, you may ask

Hi, And thanks for the reply, much appreciated.

I have had a look at a number of examples related to the AlarmManager and have taken some of this to create an alarm test code that is designed to wake the cpu and output the current time to the screen and then do this every couple of minutes:

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import java.util.Calendar;

Context context;
AlarmManager alarmMgr;
PendingIntent alarmIntent;

int repeatInt = 2;  //mins
int hrOfDay = 8; 
int minOfHr = 30;

String currentTime="";

void setup()
{
  orientation(LANDSCAPE);
  background(0);
  //stroke(255);
  textSize(50);
  
  setWakeTime();
}

void draw()
{
  background(0);
}

void setWakeTime() {
  alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
  Intent intent = new Intent(context, AlarmReceiver.class);
  alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
  
  // Set the alarm to start at some time (if in the past it starts immediately)
  Calendar calendar = Calendar.getInstance();
  calendar.setTimeInMillis(System.currentTimeMillis());
  calendar.set(Calendar.HOUR_OF_DAY, hrOfDay);  //take this info from sunrise/set time
  calendar.set(Calendar.MINUTE, minOfHr);
  
  // specify a precise custom interval
  alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
          1000 * 60 * repeatInt, alarmIntent);
}
        
public class AlarmReceiver extends BroadcastReceiver
{
  @Override
  public void onReceive(Context context, Intent intent) {
    //Toast.makeText(context,"Alarm Ring",Toast.LENGTH_SHORT).show();
    currentTime = year() + "-" + month() + "-" + day() + "  " + hour() + ":" + minute() + ":" + second();
    println ("current time> " + currentTime);
    text("current time> " + currentTime, 0, 200);
  }
}

The alarmReciever is registered in the manifest file. This code compiles but crashes with the following error:

FATAL EXCEPTION: Animation Thread
Process: processing.test.alarmmanagertest, PID: 9067
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object android.content.Context.getSystemService(java.lang.String)' on a null object reference
	at processing.test.alarmmanagertest.AlarmManagerTest.setWakeTime(AlarmManagerTest.java:76)
	at processing.test.alarmmanagertest.AlarmManagerTest.setup(AlarmManagerTest.java:67)
	at processing.core.PApplet.handleDraw(PApplet.java:1878)
	at processing.core.PSurfaceNone.callDraw(PSurfaceNone.java:478)
	at processing.core.PSurfaceNone$AnimationThread.run(PSurfaceNone.java:518)

I am not quite sure what i have done wrong here. What do i need to do to make this code work as intended?

Any help is much appreciated.

Cheers.

You have a context var which is never initialised from the looks of things.

Hi,
And sorry for not replying sooner i got stuck on other stuff but now that we are in covid-lockdown i have a chance to return to it.

I decided to approach this differently by importing the processing-core into Android Studio and creating a couple of test Apps.

I started by creating a simple alarm manger App that fires an alarm every minute (user settable) and this works fine. I then created an App that makes use of the processing-core library (imported the library as a jar dependency into android studio) and this also works.

The next step was to amalgamate the above and create an App that fires a sketch class from the broadcast receiver that includes some simple processing code but i am unsure as to how i do this.

My attempt at doing this is shown below. The only class that creates errors is the broadcast receiver class which creates errors related to frameLayout, setContentView and Fragment.setView.

I am a bit stuck, so any help with this is much appreciated.

Cheers.

All classes and XML file are shown below:

MainActivity.java

public class MainActivity extends AppCompatActivity {
    private PApplet sketch;
    Button start;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        start= findViewById(R.id.button);
 
        start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startAlert();
            }
        });
    }
 
    public void startAlert(){
        EditText text = findViewById(R.id.time);
        int i = Integer.parseInt(text.getText().toString());
        Intent intent = new Intent(this, MyBroadcastReceiver.class);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(
                this.getApplicationContext(), 234324243, intent, 0);
        AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
        alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
                + (i * 1000), pendingIntent);
        Toast.makeText(this, "Alarm set in " + i + " seconds",Toast.LENGTH_LONG).show();
    }
}

Activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.alrmmngrplusprocsktch.MainActivity">
 
    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Start"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="103dp" />
 
    <EditText
        android:id="@+id/time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="22dp"
        android:ems="10" />
</RelativeLayout>

MyBroadcastReceiver.java

public class MyBroadcastReceiver extends BroadcastReceiver {
    private PApplet sketch;
   
    @Override
    public void onReceive(Context context, Intent intent) {
        FrameLayout frame = new FrameLayout(this);
        frame.setId(CompatUtils.getUniqueViewId());
        setContentView(frame, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT));
 
        sketch = new Sketch();
        PFragment fragment = new PFragment(sketch);
        fragment.setView(frame, this);
    }
}

Sketch.java

import processing.core.PApplet;
 
public class Sketch extends PApplet
{
    int x = 0;
    double h, h2;
 
    public void settings()
    {
        fullScreen();
    }
 
    public void setup()
    {
        background(0);
        noStroke();
        fill(102);
    }
 
    public void draw()
    {
        //background(0);
 
        h = height*0.2;
        h2 = height*0.6;
        rect(x, (float)h, 1, (float)h2);
        x = x + 2;
    }
}

You might need to add some import statements, I don’t know how it works in android studio but in processing android you always need to add fragment imports, toast imports etc etc, to get your sketch to work if they make use of them.

HI,
And thanks for the reply.

One of the bonus’s of Android Studio seems to be that it automatically creates the required imports. In this case it has created the following imports for the broadcast receiver class (MyBroadcastreceiver.java):

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.view.ViewGroup;
import android.widget.FrameLayout;

import androidx.fragment.app.FragmentActivity;

import processing.android.CompatUtils;
import processing.android.PFragment;
import processing.core.PApplet;

And has flagged some as unused in the code:
import androidx.fragment.app.FragmentActivity;

The only errors in the whole program occur in the broadcast receiver code:
FrameLayout frame = new FrameLayout(this);
With associated error: Required type: Context, Provided: MyBroadcastReceiver

fragment.setView(frame, this);

With associated error: Required type: FragmentActivity, Provided: MyBroadcastReceiver

setContentView(frame, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT));

With associated error: Cannot resolve method ‘setContentView’ in ‘MyBroadcastReceiver’, which i assume means i have to add some method to the class related to layout?

Any help is much appreciated.

Cheers.