1 /*
2  * This program tries various ISOs for best ISO with a shutterspeed limit
3  *
4  * compile with: gcc -Wall -o best-iso best-iso.c -lgphoto2 -lgphoto2_port
5  *
6  */
7 
8 #include <stdlib.h>
9 #include <sys/types.h>
10 #include <sys/stat.h>
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <stdarg.h>
14 #include <string.h>
15 #include <gphoto2/gphoto2.h>
16 
17 #if !defined (O_BINARY)
18 	/*To have portable binary open() on *nix and on Windows */
19 	#define O_BINARY 0
20 #endif
21 
22 #define DEBUG
23 
24 static int aperture;
25 static float shutterspeed;
26 
errordumper(GPLogLevel level,const char * domain,const char * str,void * data)27 static void errordumper(GPLogLevel level, const char *domain, const char *str,
28                  void *data) {
29   fprintf(stdout, "%s\n", str);
30 }
31 
32 static void
ctx_error_func(GPContext * context,const char * str,void * data)33 ctx_error_func (GPContext *context, const char *str, void *data)
34 {
35         fprintf  (stderr, "\n*** Contexterror ***              \n%s\n",str);
36         fflush   (stderr);
37 }
38 
39 static void
ctx_status_func(GPContext * context,const char * str,void * data)40 ctx_status_func (GPContext *context, const char *str, void *data)
41 {
42         fprintf  (stderr, "%s\n", str);
43         fflush   (stderr);
44 }
45 
46 static
sample_create_context()47 GPContext* sample_create_context() {
48 	GPContext *context;
49 
50 	/* This is the mandatory part */
51 	context = gp_context_new();
52 
53 	/* All the parts below are optional! */
54         gp_context_set_error_func (context, ctx_error_func, NULL);
55         gp_context_set_status_func (context, ctx_status_func, NULL);
56 
57 	/* also:
58 	gp_context_set_cancel_func    (p->context, ctx_cancel_func,  p);
59         gp_context_set_message_func   (p->context, ctx_message_func, p);
60         if (isatty (STDOUT_FILENO))
61                 gp_context_set_progress_funcs (p->context,
62                         ctx_progress_start_func, ctx_progress_update_func,
63                         ctx_progress_stop_func, p);
64 	 */
65 	return context;
66 }
67 /*
68  * This function looks up a label or key entry of
69  * a configuration widget.
70  * The functions descend recursively, so you can just
71  * specify the last component.
72  */
73 
74 static int
_lookup_widget(CameraWidget * widget,const char * key,CameraWidget ** child)75 _lookup_widget(CameraWidget*widget, const char *key, CameraWidget **child) {
76 	int ret;
77 	ret = gp_widget_get_child_by_name (widget, key, child);
78 	if (ret < GP_OK)
79 		ret = gp_widget_get_child_by_label (widget, key, child);
80 	return ret;
81 }
82 
83 /* Gets a string configuration value.
84  * This can be:
85  *  - A Text widget
86  *  - The current selection of a Radio Button choice
87  *  - The current selection of a Menu choice
88  *
89  * Sample (for Canons eg):
90  *   get_config_value_string (camera, "owner", &ownerstr, context);
91  */
92 static int
get_config_value_string(Camera * camera,const char * key,char ** str,GPContext * context)93 get_config_value_string (Camera *camera, const char *key, char **str, GPContext *context) {
94 	CameraWidget		*widget = NULL, *child = NULL;
95 	CameraWidgetType	type;
96 	int			ret;
97 	char			*val;
98 
99 	ret = gp_camera_get_config (camera, &widget, context);
100 	if (ret < GP_OK) {
101 		fprintf (stderr, "camera_get_config failed: %d\n", ret);
102 		return ret;
103 	}
104 	ret = _lookup_widget (widget, key, &child);
105 	if (ret < GP_OK) {
106 		fprintf (stderr, "lookup widget failed: %d\n", ret);
107 		goto out;
108 	}
109 
110 	/* This type check is optional, if you know what type the label
111 	 * has already. If you are not sure, better check. */
112 	ret = gp_widget_get_type (child, &type);
113 	if (ret < GP_OK) {
114 		fprintf (stderr, "widget get type failed: %d\n", ret);
115 		goto out;
116 	}
117 	switch (type) {
118         case GP_WIDGET_MENU:
119         case GP_WIDGET_RADIO:
120         case GP_WIDGET_TEXT:
121 		break;
122 	default:
123 		fprintf (stderr, "widget has bad type %d\n", type);
124 		ret = GP_ERROR_BAD_PARAMETERS;
125 		goto out;
126 	}
127 
128 	/* This is the actual query call. Note that we just
129 	 * a pointer reference to the string, not a copy... */
130 	ret = gp_widget_get_value (child, &val);
131 	if (ret < GP_OK) {
132 		fprintf (stderr, "could not query widget value: %d\n", ret);
133 		goto out;
134 	}
135 	/* Create a new copy for our caller. */
136 	*str = strdup (val);
137 out:
138 	gp_widget_free (widget);
139 	return ret;
140 }
141 
142 
143 /* Sets a string configuration value.
144  * This can set for:
145  *  - A Text widget
146  *  - The current selection of a Radio Button choice
147  *  - The current selection of a Menu choice
148  *
149  * Sample (for Canons eg):
150  *   get_config_value_string (camera, "owner", &ownerstr, context);
151  */
152 static int
set_config_value_string(Camera * camera,const char * key,const char * val,GPContext * context)153 set_config_value_string (Camera *camera, const char *key, const char *val, GPContext *context) {
154 	CameraWidget		*widget = NULL, *child = NULL;
155 	CameraWidgetType	type;
156 	int			ret;
157 
158 	ret = gp_camera_get_config (camera, &widget, context);
159 	if (ret < GP_OK) {
160 		fprintf (stderr, "camera_get_config failed: %d\n", ret);
161 		return ret;
162 	}
163 	ret = _lookup_widget (widget, key, &child);
164 	if (ret < GP_OK) {
165 		fprintf (stderr, "lookup widget failed: %d\n", ret);
166 		goto out;
167 	}
168 
169 	/* This type check is optional, if you know what type the label
170 	 * has already. If you are not sure, better check. */
171 	ret = gp_widget_get_type (child, &type);
172 	if (ret < GP_OK) {
173 		fprintf (stderr, "widget get type failed: %d\n", ret);
174 		goto out;
175 	}
176 	switch (type) {
177         case GP_WIDGET_MENU:
178         case GP_WIDGET_RADIO:
179         case GP_WIDGET_TEXT:
180 		/* This is the actual set call. Note that we keep
181 		 * ownership of the string and have to free it if necessary.
182 		 */
183 		ret = gp_widget_set_value (child, val);
184 		if (ret < GP_OK) {
185 			fprintf (stderr, "could not set widget value: %d\n", ret);
186 			goto out;
187 		}
188 		break;
189         case GP_WIDGET_TOGGLE: {
190 		int ival;
191 
192 		sscanf(val,"%d",&ival);
193 		ret = gp_widget_set_value (child, &ival);
194 		if (ret < GP_OK) {
195 			fprintf (stderr, "could not set widget value: %d\n", ret);
196 			goto out;
197 		}
198 		break;
199 	}
200 	default:
201 		fprintf (stderr, "widget has bad type %d\n", type);
202 		ret = GP_ERROR_BAD_PARAMETERS;
203 		goto out;
204 	}
205 
206 	/* This stores it on the camera again */
207 	ret = gp_camera_set_config (camera, widget, context);
208 	if (ret < GP_OK) {
209 		fprintf (stderr, "camera_set_config failed: %d\n", ret);
210 		return ret;
211 	}
212 out:
213 	gp_widget_free (widget);
214 	return ret;
215 }
216 
217 
218 static int
camera_tether(Camera * camera,GPContext * context)219 camera_tether(Camera *camera, GPContext *context) {
220 	int fd, retval;
221 	CameraFile *file;
222 	CameraEventType	evttype;
223 	CameraFilePath	*path;
224 	void	*evtdata;
225 
226 	printf("Tethering...\n");
227 
228 	while (1) {
229 		retval = gp_camera_wait_for_event (camera, 2000, &evttype, &evtdata, context);
230 		if (retval != GP_OK)
231 			return retval;
232 		switch (evttype) {
233 		case GP_EVENT_FILE_ADDED:
234 			path = (CameraFilePath*)evtdata;
235 			printf("File added on the camera: %s/%s\n", path->folder, path->name);
236 
237 			fd = open(path->name, O_CREAT | O_WRONLY | O_BINARY, 0644);
238 			retval = gp_file_new_from_fd(&file, fd);
239 			printf("  Downloading %s...\n", path->name);
240 			retval = gp_camera_file_get(camera, path->folder, path->name,
241 				     GP_FILE_TYPE_NORMAL, file, context);
242 
243 			printf("  Deleting %s on camera...\n", path->name);
244 			retval = gp_camera_file_delete(camera, path->folder, path->name, context);
245 			gp_file_free(file);
246 			free(path);
247 			break;
248 		case GP_EVENT_FOLDER_ADDED:
249 			path = (CameraFilePath*)evtdata;
250 			printf("Folder added on camera: %s / %s\n", path->folder, path->name);
251 			free(path);
252 			break;
253 		case GP_EVENT_FILE_CHANGED:
254 			path = (CameraFilePath*)evtdata;
255 			printf("File changed on camera: %s / %s\n", path->folder, path->name);
256 			free(path);
257 			break;
258 		case GP_EVENT_CAPTURE_COMPLETE:
259 			printf("Capture Complete.\n");
260 			break;
261 		case GP_EVENT_TIMEOUT:
262 			printf("Timeout.\n");
263 			return GP_OK;
264 		case GP_EVENT_UNKNOWN:
265 			if (evtdata) {
266 				char *val;
267 				int ret;
268 
269 				/* printf("Unknown event: %s.\n", (char*)evtdata); */
270 
271 				/* 3 eos properties hardcoded */
272 				if (strstr(evtdata,"d103")) {
273 					ret = get_config_value_string(camera,"iso",&val,context);
274 					if (ret == GP_OK) {
275 						printf("ISO is %s\n", val);
276 						free(val);
277 					}
278 				}
279 				if (strstr(evtdata,"d102")) {
280 					ret = get_config_value_string(camera,"shutterspeed",&val,context);
281 					if (ret == GP_OK) {
282 						if (strchr(val,'/')) {
283 							int zaehler,nenner;
284 							sscanf(val,"%d/%d",&zaehler,&nenner);
285 							shutterspeed = 1.0*zaehler/nenner;
286 						} else {
287 							if (!sscanf(val,"%g",&shutterspeed))
288 								shutterspeed = 0.0;
289 						}
290 						printf("Shutterspeed is %s (%g)\n", val, shutterspeed);
291 						free(val);
292 					}
293 				}
294 				if (strstr(evtdata,"d101")) {
295 					ret = get_config_value_string(camera,"aperture",&val,context);
296 					if (ret == GP_OK) {
297 						float ap;
298 
299 						sscanf(val,"%g",&ap);
300 						aperture = 10*ap;
301 						printf("Aperture is %s (%d)\n", val, aperture);
302 						free (val);
303 					}
304 				}
305 			} else {
306 				printf("Unknown event.\n");
307 			}
308 			break;
309 		default:
310 			printf("Type %d?\n", evttype);
311 			break;
312 		}
313 	}
314 }
315 
316 int
main(int argc,char ** argv)317 main(int argc, char **argv) {
318 	Camera	*camera;
319 	int	retval, iso, tries;
320 	char	buf[20];
321 	GPContext *context = sample_create_context();
322         /*int	fd;*/
323         CameraFile *file;
324         /*CameraFilePath camera_file_path;*/
325 
326 	gp_log_add_func(GP_LOG_ERROR, errordumper, NULL);
327 	gp_camera_new(&camera);
328 
329 	retval = gp_camera_init(camera, context);
330 	if (retval != GP_OK) {
331 		printf("  camera failed to initialize: %d\n", retval);
332 		exit (1);
333 	}
334 
335 	retval = camera_tether(camera, context);
336 	if (retval != GP_OK) {
337 		printf("Tether error: %d\n", retval);
338 		exit (1);
339 	}
340 
341 	iso = 100;
342 
343 	shutterspeed = 0.0;
344 	aperture = 0;
345 
346 #if 1
347 	gp_file_new (&file);
348 	retval = gp_camera_capture_preview (camera, file, context);
349 	if (retval != GP_OK) {
350 		printf("Preview capture error: %d\n", retval);
351 		exit (1);
352 	}
353 #endif
354 
355 	while (1) {
356 		sprintf(buf,"%d",iso);
357 		printf("Setting ISO to %d\n",iso);
358 
359 		retval = set_config_value_string(camera, "iso", buf, context);
360 		if (retval != GP_OK) {
361 			printf("  failed setting ISO to %d: %d\n", iso, retval);
362 
363 			/* usually also happens if too dark */
364 			exit (1);
365 		}
366 
367 		printf("eosremoterelease half press\n");
368 		retval = set_config_value_string(camera,"eosremoterelease", "Press Half", context);
369 		if (retval != GP_OK) {
370 			printf("  failed pressing shutter to half: %d\n", retval);
371 			exit (1);
372 		}
373 
374 		retval = camera_tether(camera, context);
375 		if (retval != GP_OK) {
376 			printf("Tether error: %d\n", retval);
377 			exit (1);
378 		}
379 
380 		if (shutterspeed < 0.000000001) {
381 			printf("Camera did not report shutterspeed?\n");
382 		}
383 
384 		printf("eosremoterelease release\n");
385 		retval = set_config_value_string(camera,"eosremoterelease", "Release Half", context);
386 		if (retval != GP_OK) {
387 			printf("  failed releasing shutter button from half: %d\n", retval);
388 			exit (1);
389 		}
390 
391 		if (shutterspeed < 30.0) {
392 
393 			printf("ISO %d gets Aperture %g and Shutterspeed %g\n", iso, aperture/10.0, shutterspeed);
394 
395 			break;
396 		}
397 		/* ISO always goes up in 2 multiplicator steps */
398 		iso = iso*2;
399 	}
400 
401 	/* we can take the picture now */
402 
403 #if 0
404 
405         printf("Capturing.\n");
406 
407         retval = gp_camera_capture(camera, GP_CAPTURE_IMAGE, &camera_file_path, context);
408         if (retval != GP_OK) {
409 		printf("  capture failed: %d\n", retval);
410 		exit(1);
411 	}
412 
413         printf("Pathname on the camera: %s/%s\n", camera_file_path.folder, camera_file_path.name);
414 
415         fd = open("capture.jpg", O_CREAT | O_WRONLY | O_BINARY, 0644);
416 
417         retval = gp_file_new_from_fd(&file, fd);
418         if (retval != GP_OK) printf("  gp_file_new failed: %d\n", retval);
419 
420         retval = gp_camera_file_get(camera, camera_file_path.folder, camera_file_path.name,
421                      GP_FILE_TYPE_NORMAL, file, context);
422         if (retval != GP_OK) printf("  file_get failed: %d\n", retval);
423 
424         printf("Deleting downloaded image.\n");
425         retval = gp_camera_file_delete(camera, camera_file_path.folder, camera_file_path.name,
426                         context);
427         if (retval != GP_OK) printf("  Retval: %d\n", retval);
428 
429         gp_file_free(file);
430 
431 #endif
432 #if 1
433 	/* SAMPLE Capture code with eosremoterelease ... normal gp_camera_capture_image also works. */
434 	printf("eosremoterelease release\n");
435 	retval = set_config_value_string(camera,"eosremoterelease", "Immediate", context);
436 
437 	if (retval != GP_OK) {
438 		printf("  failed pressing shutter button full: %d\n", retval);
439 		exit (1);
440 	}
441 
442 	retval = set_config_value_string(camera,"eosremoterelease", "Release Full", context);
443 
444 	if (retval != GP_OK) {
445 		printf("  failed releasing shutter button full: %d\n", retval);
446 		exit (1);
447 	}
448 
449 
450 	retval = camera_tether(camera, context);
451 	if (retval != GP_OK) {
452 		printf("Tether error: %d\n", retval);
453 		exit (1);
454 	}
455 #endif
456 
457 	tries = 30; /* seconds at most, discounting events. */
458 	while (tries--) {
459 		retval = camera_tether(camera, context);
460 		if (retval != GP_OK) {
461 			printf("Tether error: %d\n", retval);
462 			exit (1);
463 		}
464 	}
465 
466 #if 1
467 	retval = set_config_value_string(camera,"eosviewfinder", "0", context);
468 	if (retval != GP_OK) {
469 		printf("setting eosviewfinder off error: %d\n", retval);
470 		exit (1);
471 	}
472 #endif
473 
474 	gp_camera_exit(camera, context);
475 	return 0;
476 }
477