diff --git a/exec/mqtt_pub.js b/exec/mqtt_pub.js
new file mode 100644
index 0000000000000000000000000000000000000000..25c8acab45b9edff7a7f523acbcf1e341ff41e3e
--- /dev/null
+++ b/exec/mqtt_pub.js
@@ -0,0 +1,74 @@
+// A simple MQTT publisher, for use with jsexec
+// Command-line options more or less mimic 'mosquitto_pub'
+
+"use strict";
+
+var mqtt = new MQTT;
+var topic;
+var msg;
+var retain = false;
+
+function usage()
+{
+	print("options:");
+	print("  -h  MQTT broker hostname or IP address, default: " + mqtt.broker_addr);
+	print("  -p  MQTT TCP port number to use, default: " + mqtt.broker_port);
+	print("  -q  QoS value to use, default: " + mqtt.publish_qos);
+	print("  -r  Retain message");
+	print("  -t  Topic to publish message to (required)");
+	print("  -m  Message text to publish");
+	print("  -n  send a null (zero length) message");
+	exit(0);
+}
+
+for(var i = 0; i < argc; ++i) {
+	var arg = argv[i];
+	while(arg[0] === '-')
+		arg = arg.substring(1);
+	switch(arg) {
+		case 'h':
+			mqtt.broker_addr = argv[++i];
+			break;
+		case 'm':
+			msg = argv[++i];
+			break;
+		case 'n':
+			msg = '';
+			break;
+		case 'p':
+			mqtt.broker_port = parseInt(argv[++i], 10);
+			break;
+		case 'q':
+			mqtt.publish_qos = parseInt(argv[++i], 10);
+			break;
+		case 'r':
+			retain = true;
+			break;
+		case 't':
+			topic = argv[++i];
+			break;
+		case 'v':
+			verbose = true;
+			break;
+		case '?':
+		case 'help':
+			usage();
+		default:
+			options[argv[i].slice(1)] = true;
+			break;
+	}
+}
+if(msg === undefined || !topic)
+	usage();
+if(!mqtt.connect()) {
+	alert(format("Error (%s) connecting to %s:%u", mqtt.error_str, mqtt.broker_addr, mqtt.broker_port));
+	exit(1);
+}
+//prompt("hit enter");
+
+if(!mqtt.publish(retain, topic, msg)) {
+	alert(format("Error (%s) publishing to %s", mqtt.error_str, topic));
+	exit(1);
+}
+
+//prompt("hit enter");