Files
solarlife/main.c
2023-10-20 21:31:26 +02:00

936 lines
22 KiB
C

#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <limits.h>
#include <errno.h>
#include "mqtt.h"
#include "client.h"
#include <assert.h>
#include "bluetooth.h"
#include "hci.h"
#include "hci_lib.h"
#include "l2cap.h"
#include "uuid.h"
#include <sys/ioctl.h>
#include "mainloop.h"
#include "util.h"
#include "att.h"
#include "queue.h"
#include "gatt-db.h"
#include "gatt-client.h"
#include "json.h"
#define ATT_CID 4
#define PRLOG(...) \
printf(__VA_ARGS__);
#define JSONOUTLEN 4096
#define MQTT_SEND_PERIOD_S 10
static uint16_t mqtt_port = 1883;
static uint16_t send_interval = MQTT_SEND_PERIOD_S;
static bool verbose = false;
static int handler_registered=0;
struct client *cli;
uint8_t rcv_buf[256];
char jsonout[JSONOUTLEN];
struct JsonVal top;
uint8_t rcv_ptr=0, frame_len=0;
uint16_t addr_base=0;
char mqtt_buffer[BUFFER_SIZE_BYTES];
client_t c;
//int is_subscriber = 1;
int keepalive_sec = 4;
int nbytes;
struct client {
/// socket
int fd;
/// pointer to a bt_att structure
struct bt_att *att;
/// pointer to a gatt_db structure
struct gatt_db *db;
/// pointer to a bt_gatt_client structure
struct bt_gatt_client *gatt;
/// session id
unsigned int reliable_session_id;
};
static void got_connection(client_t* psClnt)
{
uint8_t buf[128];
require(psClnt != 0);
if(!psClnt->sockfd) return;
printf("MQTT: connected to server.\n");
nbytes = mqtt_encode_connect_msg2(buf, 0x02, keepalive_sec, (uint8_t*)"Solarlife", 9);
client_send(&c, (char*)buf, nbytes);
}
static void lost_connection(client_t* psClnt)
{
require(psClnt != 0);
printf("MQTT: disconnected from server.\n");
mainloop_quit();
}
static void got_data(client_t* psClnt, unsigned char* data, int nbytes)
{
require(psClnt != 0);
uint8_t ctrl = data[0] >> 4;
if (ctrl == CTRL_PUBACK) {
printf("MQTT server acknowledged data\n");
}
}
int16_t b2int (uint8_t *ptr) {
return ptr[1] | ptr[0] << 8;
}
int b2int2 (uint8_t *ptr) {
return ptr[0] | ptr[1] << 8;
}
uint16_t ModRTU_CRC(uint8_t* buf, int len)
{
uint16_t crc = 0xFFFF;
for (int pos = 0; pos < len; pos++) {
crc ^= (uint16_t)buf[pos]; // XOR byte into least sig. byte of crc
for (int i = 8; i != 0; i--) { // Loop over each bit
if ((crc & 0x0001) != 0) { // If the LSB is set
crc >>= 1; // Shift right and XOR 0xA001
crc ^= 0xA001;
}
else // Else LSB is not set
crc >>= 1; // Just shift right
}
}
// Note, this number has low and high bytes swapped, so use it accordingly (or swap bytes)
return crc;
}
static void write_cb(bool success, uint8_t att_ecode, void *user_data)
{
/*
if (success) {
PRLOG("\nWrite successful\n");
} else {
PRLOG("\nWrite failed: %s (0x%02x)\n",
ecode_to_string(att_ecode), att_ecode);
}
*/
}
int send_modbus_req(uint8_t eq_id, uint8_t fct_code,uint16_t addr, uint16_t len) {
uint8_t buf[10];
buf[0]=eq_id;
buf[1]=fct_code;
buf[2]=addr>>8;
buf[3]=addr&0xff;
buf[4]=len>>8;
buf[5]=len&0xff;
uint16_t crc = ModRTU_CRC(buf,6);
buf[7]=crc>>8;
buf[6]=crc&0xff;
addr_base = addr;
rcv_ptr = 0; //reset pointer
top = jsonCreateObject();
if (!bt_gatt_client_write_value(cli->gatt, 0x14, buf, 8, write_cb, NULL, NULL)) {
printf("Failed to initiate write procedure\n");
return 1;
}
return 0;
}
static void log_service_event(struct gatt_db_attribute *attr, const char *str)
{
char uuid_str[MAX_LEN_UUID_STR];
bt_uuid_t uuid;
uint16_t start, end;
gatt_db_attribute_get_service_uuid(attr, &uuid);
bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
gatt_db_attribute_get_service_handles(attr, &start, &end);
PRLOG("%s - UUID: %s start: 0x%04x end: 0x%04x\n", str, uuid_str,
start, end);
}
static void register_notify_cb(uint16_t att_ecode, void *user_data)
{
if (att_ecode) {
PRLOG("Failed to register notify handler "
"- error code: 0x%02x\n", att_ecode);
return;
}
PRLOG("Registered notify handler!\n");
handler_registered = 1;
}
void parse_val(uint8_t *buf, uint16_t addr) {
//printf("Parsing value at address %04x\n",addr);
uint8_t ptr = addr*2+3;
addr+=addr_base;
switch (addr) {
case 0x3000:
JsonVal_objectAddNumber(&top, "PV_rated_voltage", (float)b2int(buf+ptr)/100);
break;
case 0x3001:
JsonVal_objectAddNumber(&top, "PV_rated_current", (float)b2int(buf+ptr)/100);
break;
case 0x3002:
JsonVal_objectAddNumber(&top, "PV_rated_power_l", (float)b2int(buf+ptr)/100);
break;
case 0x3003:
JsonVal_objectAddNumber(&top, "PV_rated_power_h", (float)b2int(buf+ptr)/100);
break;
case 0x3004:
JsonVal_objectAddNumber(&top, "battery_rated_voltage", (float)b2int(buf+ptr)/100);
break;
case 0x3005:
JsonVal_objectAddNumber(&top, "battery_rated_current", (float)b2int(buf+ptr)/100);
break;
case 0x3006:
JsonVal_objectAddNumber(&top, "battery_rated_power_l", (float)b2int(buf+ptr)/100);
break;
case 0x3007:
JsonVal_objectAddNumber(&top, "battery_rated_power_h", (float)b2int(buf+ptr)/100);
break;
case 0x3008:
JsonVal_objectAddNumber(&top, "load_rated_voltage", (float)b2int(buf+ptr)/100);
break;
case 0x3009:
JsonVal_objectAddNumber(&top, "load_rated_current", (float)b2int(buf+ptr)/100);
break;
case 0x300a:
JsonVal_objectAddNumber(&top, "load_rated_power_l", (float)b2int(buf+ptr)/100);
break;
case 0x300b:
JsonVal_objectAddNumber(&top, "load_rated_power_h", (float)b2int(buf+ptr)/100);
break;
case 0x3030:
JsonVal_objectAddNumber(&top, "slave_id", (int)b2int(buf+ptr));
break;
case 0x3031:
JsonVal_objectAddNumber(&top, "running_days", (int)b2int(buf+ptr));
break;
case 0x3032:
JsonVal_objectAddNumber(&top, "sys_voltage", (float)b2int(buf+ptr)/100);
break;
case 0x3033:
JsonVal_objectAddNumber(&top, "battery_status", b2int(buf+ptr));
break;
case 0x3034:
JsonVal_objectAddNumber(&top, "charge_status", b2int(buf+ptr));
break;
case 0x3035:
JsonVal_objectAddNumber(&top, "discharge_status", b2int(buf+ptr));
break;
case 0x3036:
JsonVal_objectAddNumber(&top, "env_temperature", (float)b2int(buf+ptr)/100);
break;
case 0x3037:
JsonVal_objectAddNumber(&top, "sys_temperature", (float)b2int(buf+ptr)/100);
break;
case 0x3038:
JsonVal_objectAddNumber(&top, "undervoltage_times", b2int(buf+ptr));
break;
case 0x3039:
JsonVal_objectAddNumber(&top, "fullycharged_times", b2int(buf+ptr));
break;
case 0x303a:
JsonVal_objectAddNumber(&top, "overvoltage_prot_times", b2int(buf+ptr));
break;
case 0x303b:
JsonVal_objectAddNumber(&top, "overcurrent_prot_times", b2int(buf+ptr));
break;
case 0x303c:
JsonVal_objectAddNumber(&top, "shortcircuit_prot_times", b2int(buf+ptr));
break;
case 0x303d:
JsonVal_objectAddNumber(&top, "opencircuit_prot_times", b2int(buf+ptr));
break;
case 0x303e:
JsonVal_objectAddNumber(&top, "hw_prot_times", b2int(buf+ptr));
break;
case 0x303f:
JsonVal_objectAddNumber(&top, "charge_overtemp_prot_times", b2int(buf+ptr));
break;
case 0x3040:
JsonVal_objectAddNumber(&top, "discharge_overtemp_prot_times", b2int(buf+ptr));
break;
case 0x3045:
JsonVal_objectAddNumber(&top, "battery_remaining_capacity", b2int(buf+ptr));
break;
case 0x3046:
JsonVal_objectAddNumber(&top, "battery_voltage", (float)b2int(buf+ptr)/100);
break;
case 0x3047:
JsonVal_objectAddNumber(&top, "battery_current", (float)b2int(buf+ptr)/100);
//JsonVal_objectAddNumber(&top, "battery_current", b2int(buf+ptr)/100);
//printf("current: %d\n",b2int(buf+ptr));
break;
case 0x3048:
JsonVal_objectAddNumber(&top, "battery_power_lo", (float)b2int(buf+ptr)/100);
break;
case 0x3049:
JsonVal_objectAddNumber(&top, "battery_power_hi", (float)b2int(buf+ptr)/100);
break;
case 0x304a:
JsonVal_objectAddNumber(&top, "load_voltage", (float)b2int(buf+ptr)/100);
break;
case 0x304b:
JsonVal_objectAddNumber(&top, "load_current", (float)b2int(buf+ptr)/100);
break;
case 0x304c:
JsonVal_objectAddNumber(&top, "load_power_l", (float)b2int(buf+ptr)/100);
break;
case 0x304d:
JsonVal_objectAddNumber(&top, "load_power_h", (float)b2int(buf+ptr)/100);
break;
case 0x304e:
JsonVal_objectAddNumber(&top, "solar_voltage", (float)b2int(buf+ptr)/100);
break;
case 0x304f:
JsonVal_objectAddNumber(&top, "solar_current", (float)b2int(buf+ptr)/100);
break;
case 0x3050:
JsonVal_objectAddNumber(&top, "solar_power_l", (float)b2int(buf+ptr)/100);
break;
case 0x3051:
JsonVal_objectAddNumber(&top, "solar_power_h", (float)b2int(buf+ptr)/100);
break;
case 0x3052:
JsonVal_objectAddNumber(&top, "daily_production", (float)b2int(buf+ptr)/100);
break;
case 0x3053:
JsonVal_objectAddNumber(&top, "total_production_l", (float)b2int(buf+ptr)/100);
break;
case 0x3054:
JsonVal_objectAddNumber(&top, "total_production_h", (float)b2int(buf+ptr)/100);
break;
case 0x3055:
JsonVal_objectAddNumber(&top, "daily_consumption", (float)b2int(buf+ptr)/100);
break;
case 0x3056:
JsonVal_objectAddNumber(&top, "total_consumption_l", (float)b2int(buf+ptr)/100);
break;
case 0x3057:
JsonVal_objectAddNumber(&top, "total_consumption_h", (float)b2int(buf+ptr)/100);
break;
case 0x3058:
JsonVal_objectAddNumber(&top, "lighttime_daily", b2int(buf+ptr));
break;
case 0x305d:
JsonVal_objectAddNumber(&top, "monthly_production_l", (float)b2int(buf+ptr)/100);
break;
case 0x305e:
JsonVal_objectAddNumber(&top, "monthly_production_h", (float)b2int(buf+ptr)/100);
break;
case 0x305f:
JsonVal_objectAddNumber(&top, "yearly_production_l", (float)b2int(buf+ptr)/100);
break;
case 0x3060:
JsonVal_objectAddNumber(&top, "yearly_production_h", (float)b2int(buf+ptr)/100);
break;
case 0x9017:
JsonVal_objectAddNumber(&top, "rtc_second", b2int(buf+ptr));
break;
case 0x9018:
JsonVal_objectAddNumber(&top, "rtc_minute", b2int(buf+ptr));
break;
case 0x9019:
JsonVal_objectAddNumber(&top, "rtc_hour", b2int(buf+ptr));
break;
case 0x901a:
JsonVal_objectAddNumber(&top, "rtc_day", b2int(buf+ptr));
break;
case 0x901b:
JsonVal_objectAddNumber(&top, "rtc_month", b2int(buf+ptr));
break;
case 0x901c:
JsonVal_objectAddNumber(&top, "rtc_year", b2int(buf+ptr));
break;
case 0x901d:
JsonVal_objectAddNumber(&top, "baud_rate", b2int(buf+ptr));
break;
case 0x901f:
JsonVal_objectAddNumber(&top, "password", b2int(buf+ptr));
break;
case 0x9021:
JsonVal_objectAddNumber(&top, "battery_type", b2int(buf+ptr));
break;
case 0x9022:
JsonVal_objectAddNumber(&top, "lvp_voltage", (float)b2int(buf+ptr)/100);
break;
case 0x9023:
JsonVal_objectAddNumber(&top, "lvr_voltage", (float)b2int(buf+ptr)/100);
break;
case 0x9024:
JsonVal_objectAddNumber(&top, "boost_voltage", (float)b2int(buf+ptr)/100);
break;
case 0x9025:
JsonVal_objectAddNumber(&top, "equalizing_voltage", (float)b2int(buf+ptr)/100);
break;
case 0x9026:
JsonVal_objectAddNumber(&top, "floating_voltage", (float)b2int(buf+ptr)/100);
break;
default:
break;
}
}
static void notify_cb(uint16_t value_handle, const uint8_t *value,
uint16_t length, void *user_data)
{
memcpy(rcv_buf+rcv_ptr, value, length);
rcv_ptr+=length;
if (rcv_ptr==rcv_buf[2]+5) {
uint16_t crc_r = b2int2(rcv_buf+rcv_ptr-2);
uint16_t crc = ModRTU_CRC(rcv_buf, rcv_ptr-2);
if((crc==crc_r) && addr_base) { //CRC check passed
for (int ii = 0; ii<(rcv_buf[2]-2)/2; ii++) {
parse_val(rcv_buf,ii);
}
time_t now;
time(&now);
char buf[sizeof "2011-10-08T07:07:09Z"];
strftime(buf, sizeof buf, "%FT%TZ", gmtime(&now));
JsonVal_objectAddString(&top, "timestamp", buf);
JsonVal_writeString(&top, jsonout, JSONOUTLEN);
puts(jsonout);
char buf2[8000];
nbytes = mqtt_encode_publish_msg((uint8_t*)buf2, (uint8_t*)"Solarlife",9 , 1, 10, (uint8_t*)jsonout, strlen(jsonout));
if(c.sockfd) {
client_send(&c, buf2, nbytes);
client_poll(&c, 1000000);
}
JsonVal_destroy(&top);
//if(addr_base<0x9000) {
// send_modbus_req(0x01, 0x03, 0x9000, 0x40);
// addr_base = 0;
//}
}
}
}
static void ready_cb(bool success, uint8_t att_ecode, void *user_data)
{
struct client *cli = user_data;
if (!success) {
PRLOG("GATT discovery procedures failed - error code: 0x%02x\n",
att_ecode);
return;
}
PRLOG("GATT discovery procedures complete\n");
unsigned char buf[] = {01,00};
if (!bt_gatt_client_write_value(cli->gatt, 0x12, buf, sizeof(buf),
write_cb,
NULL, NULL))
printf("Failed to initiate write procedure\n");
unsigned int id = bt_gatt_client_register_notify(cli->gatt, 0x11,
register_notify_cb,
notify_cb, NULL, NULL);
if (!id) {
printf("Failed to register notify handler\n");
}
PRLOG("Registering notify handler with id: %u\n", id);
}
static void service_removed_cb(struct gatt_db_attribute *attr, void *user_data)
{
log_service_event(attr, "Service Removed");
}
static void service_added_cb(struct gatt_db_attribute *attr, void *user_data)
{
log_service_event(attr, "Service Added");
}
static void att_disconnect_cb(int err, void *user_data)
{
printf("Device disconnected: %s\n", strerror(err));
mainloop_quit();
}
void send_mqtt_ping(void) {
unsigned char buf[100];
nbytes = mqtt_encode_ping_msg(buf);
if ((nbytes != 0) && (c.sockfd))
{
client_send(&c, (char*)buf, nbytes);
client_poll(&c, 0);
}
}
static void signal_cb(int signum, void *user_data)
{
static int sec_counter;
switch (signum) {
case SIGINT:
case SIGTERM:
mainloop_quit();
break;
case SIGALRM:
{
//if(addr_base) send_modbus_req(0x01, 0x03, 0x9000, 0x40);
//else {
// send_modbus_req(0x01, 0x04, 0x3000, 0x7c);
// addr_base=0;
//}
//uint16_t len = form_modbus_msg(buff,0x01, 0x03, 0x9000, 0x40);
//send_modbus_req(0x01, 0x04, 0x3000, 0x7c);
//send_modbus_req(0x01, 0x04, 0x3047, 0x20);
send_mqtt_ping();
if(handler_registered) {
if (++sec_counter>send_interval) {
send_modbus_req(0x01, 0x04, 0x3000, 0x7c);
sec_counter=0;
}
}
alarm(1);
break;
}
default:
break;
}
}
static void client_destroy(struct client *cli)
{
bt_gatt_client_unref(cli->gatt);
bt_att_unref(cli->att);
free(cli);
}
static struct client *client_create(int fd, uint16_t mtu)
{
struct client *cli;
cli = new0(struct client, 1);
if (!cli) {
fprintf(stderr, "Failed to allocate memory for client\n");
return NULL;
}
cli->att = bt_att_new(fd, false);
if (!cli->att) {
fprintf(stderr, "Failed to initialze ATT transport layer\n");
bt_att_unref(cli->att);
free(cli);
return NULL;
}
if (!bt_att_set_close_on_unref(cli->att, true)) {
fprintf(stderr, "Failed to set up ATT transport layer\n");
bt_att_unref(cli->att);
free(cli);
return NULL;
}
if (!bt_att_register_disconnect(cli->att, att_disconnect_cb, NULL,
NULL)) {
fprintf(stderr, "Failed to set ATT disconnect handler\n");
bt_att_unref(cli->att);
free(cli);
return NULL;
}
cli->fd = fd;
cli->db = gatt_db_new();
if (!cli->db) {
fprintf(stderr, "Failed to create GATT database\n");
bt_att_unref(cli->att);
free(cli);
return NULL;
}
cli->gatt = bt_gatt_client_new(cli->db, cli->att, mtu);
if (!cli->gatt) {
fprintf(stderr, "Failed to create GATT client\n");
gatt_db_unref(cli->db);
bt_att_unref(cli->att);
free(cli);
return NULL;
}
gatt_db_register(cli->db, service_added_cb, service_removed_cb,
NULL, NULL);
bt_gatt_client_set_ready_handler(cli->gatt, ready_cb, cli, NULL);
/* bt_gatt_client already holds a reference */
gatt_db_unref(cli->db);
return cli;
}
static int l2cap_le_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type,
int sec)
{
int sock;
struct sockaddr_l2 srcaddr, dstaddr;
struct bt_security btsec;
sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
if (sock < 0) {
perror("Failed to create L2CAP socket");
return -1;
}
/* Set up source address */
memset(&srcaddr, 0, sizeof(srcaddr));
srcaddr.l2_family = AF_BLUETOOTH;
srcaddr.l2_cid = htobs(ATT_CID);
srcaddr.l2_bdaddr_type = 0;
bacpy(&srcaddr.l2_bdaddr, src);
if (bind(sock, (struct sockaddr *)&srcaddr, sizeof(srcaddr)) < 0) {
perror("Failed to bind L2CAP socket");
close(sock);
return -1;
}
/* Set the security level */
memset(&btsec, 0, sizeof(btsec));
btsec.level = sec;
if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &btsec,
sizeof(btsec)) != 0) {
fprintf(stderr, "Failed to set L2CAP security level\n");
close(sock);
return -1;
}
/* Set up destination address */
memset(&dstaddr, 0, sizeof(dstaddr));
dstaddr.l2_family = AF_BLUETOOTH;
dstaddr.l2_cid = htobs(ATT_CID);
dstaddr.l2_bdaddr_type = dst_type;
bacpy(&dstaddr.l2_bdaddr, dst);
printf("Connecting to BT device...");
fflush(stdout);
if (connect(sock, (struct sockaddr *) &dstaddr, sizeof(dstaddr)) < 0) {
perror(" Failed to connect to BT device");
close(sock);
return -1;
}
printf(" Done\n");
return sock;
}
void inthandler(int dummy)
{
(void)dummy;
uint8_t buf[128];
int nbytes = mqtt_encode_disconnect_msg(buf);
if (nbytes != 0)
{
client_send(&c, (char*)buf, nbytes);
}
client_poll(&c, 10000000);
exit(0);
}
static void usage(char *argv[])
{
printf("%s\n",argv[0]);
printf("Usage:\n\t%s [options]\n",argv[0]);
printf("Options:\n"
"\t-i, --index <id>\t\tSpecify adapter index, e.g. hci0\n"
"\t-d, --dest <addr>\t\tSpecify the destination mac address of the Solarlife device\n"
"\t-a, --host <ipaddr>\t\tSpecify the MQTT broker address\n"
"\t-p, --port \t\t\tMQTT broker port, default: %d\n"
"\t-t, --interval \t\t\tSet data sending interval in seconds, default: %d s\n"
"\t-v, --verbose\t\t\tEnable extra logging\n"
"\t-h, --help\t\t\tDisplay help\n",mqtt_port, send_interval);
printf("Example:\n"
"%s -d C4:BE:84:70:29:04 -a test.mosquitto.org -t 10\n",argv[0]);
}
static struct option main_options[] = {
{ "index", 1, 0, 'i' },
{ "dest", 1, 0, 'd' },
{ "host", 1, 0, 'a' },
{ "port", 1, 0, 'p' },
{ "interval", 1, 0, 't' },
{ "verbose", 0, 0, 'v' },
{ "help", 0, 0, 'h' },
{ }
};
int main(int argc, char *argv[])
{
int opt;
int sec = BT_SECURITY_LOW;
uint16_t mtu = 0;
uint8_t dst_type = BDADDR_LE_PUBLIC;
bool dst_addr_given = false;
bdaddr_t src_addr, dst_addr;
int dev_id = -1;
int fd;
sigset_t mask;
char mqtt_host[100];
//str2ba("04:7f:0e:54:61:0c", &dst_addr);
while ((opt = getopt_long(argc, argv, "+hva:t:d:i:p:",
main_options, NULL)) != -1) {
switch (opt) {
case 'h':
usage(argv);
return EXIT_SUCCESS;
case 'v':
verbose = true;
break;
case 'a':
strncpy(mqtt_host,optarg,sizeof(mqtt_host));
break;
case 'p': {
int arg;
arg = atoi(optarg);
if (arg <= 0) {
fprintf(stderr, "Invalid MQTT port: %d\n", arg);
return EXIT_FAILURE;
}
if (arg > UINT16_MAX) {
fprintf(stderr, "MQTT port too large: %d\n", arg);
return EXIT_FAILURE;
}
mqtt_port = (uint16_t)arg;
break;
}
case 't': {
int arg;
arg = atoi(optarg);
if (arg <= 0) {
fprintf(stderr, "Invalid interval: %d s\n", arg);
return EXIT_FAILURE;
}
if (arg > UINT16_MAX) {
fprintf(stderr, "Interval too large: %d s\n", arg);
return EXIT_FAILURE;
}
if (arg < 1) {
fprintf(stderr, "Interval too small: %d s\n", arg);
return EXIT_FAILURE;
}
send_interval = (uint16_t)arg;
break;
}
case 'd':
if (str2ba(optarg, &dst_addr) < 0) {
fprintf(stderr, "Invalid Solarlife address: %s\n",
optarg);
return EXIT_FAILURE;
}
dst_addr_given = true;
break;
case 'i':
dev_id = hci_devid(optarg);
if (dev_id < 0) {
perror("Invalid adapter");
return EXIT_FAILURE;
}
break;
default:
//fprintf(stderr, "Invalid option: %c\n", opt);
return EXIT_FAILURE;
}
}
if (!argc) {
usage(argv);
return EXIT_SUCCESS;
}
argc -= optind;
argv += optind;
optind = 0;
if (argc) {
usage(argv);
return EXIT_SUCCESS;
}
if (dev_id == -1)
bacpy(&src_addr, BDADDR_ANY);
else if (hci_devba(dev_id, &src_addr) < 0) {
perror("Adapter not available");
return EXIT_FAILURE;
}
if (!dst_addr_given) {
fprintf(stderr, "Destination BT mac address required! Use -h option to print usage.\n");
return EXIT_FAILURE;
}
/* create the mainloop resources */
mainloop_init();
fd = l2cap_le_att_connect(&src_addr, &dst_addr, dst_type, sec);
if (fd < 0)
return EXIT_FAILURE;
cli = client_create(fd, mtu);
if (!cli) {
close(fd);
return EXIT_FAILURE;
}
if(strlen(mqtt_host)) {
client_init(&c, mqtt_host, mqtt_port, mqtt_buffer, BUFFER_SIZE_BYTES);
assert(client_set_callback(&c, CB_RECEIVED_DATA, got_data) == 1);
assert(client_set_callback(&c, CB_ON_CONNECTION, got_connection) == 1);
assert(client_set_callback(&c, CB_ON_DISCONNECT, lost_connection) == 1);
signal(SIGINT, inthandler);
client_connect(&c);
} else {
printf("MQTT Broker address not given, will not send data.\n");
}
alarm(1);
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGTERM);
sigaddset(&mask, SIGALRM);
/* add handler for process interrupted (SIGINT) or terminated (SIGTERM)*/
mainloop_set_signal(&mask, signal_cb, NULL, NULL);
mainloop_run();
printf("\n\nShutting down...\n");
if(c.sockfd) client_disconnect(&c);
client_destroy(cli);
return EXIT_SUCCESS;
}