/* * Copyright (C) 2015, 2016 "IoT.bzh" * Author "Manuel Bachmann" * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #define CAN_DEV "can0" const struct afb_binding_interface *interface; // Initialize CAN hvac array that will be sent trough the socket char *can_hvac_components[8] = { "LeftTemperature", "RightTemperature", "Temperature", "Unknow3", "FanSpeed", "Unknow5", "Unknow6", "Unknow7" }; // Initialize CAN hvac array that will be sent trough the socket __u8 can_hvac_values[8] = { 21, 21, 21, 240, // Don't know why 240 but it was 0xF0 in the original amb hvacplugin 0, 1, // Don't know why 1 but it was 0xF0 in the original amb hvacplugin 0, // Don't know why 0 but it was 0xF0 in the original amb hvacplugin 0 // Don't know why 0 but it was 0xF0 in the original amb hvacplugin }; struct can_handler{ int socket; struct sockaddr_can txAddress; }; /***************************************************************************************/ /***************************************************************************************/ /** **/ /** **/ /** SECTION: HANDLE CAN DEVICE **/ /** **/ /** **/ /***************************************************************************************/ /***************************************************************************************/ static struct can_handler open_can_dev() { struct can_handler ch; ch.socket = -1; ch.socket = socket(PF_CAN, SOCK_RAW, CAN_RAW); if (ch.socket < 0) { printf("ERROR: socket could not be created\n"); } else { struct ifreq ifr; // Attemts to open a socket to CAN bus strcpy(ifr.ifr_name, CAN_DEV); if(ioctl(ch.socket, SIOCGIFINDEX, &ifr) < 0) { close(ch.socket); ch.socket = -1; printf("ERROR: ioctl failed\n"); } else { ch.txAddress.can_family = AF_CAN; ch.txAddress.can_ifindex = ifr.ifr_ifindex; // And bind it to ch.txAddress struct if (bind(ch.socket, (struct sockaddr *)&ch.txAddress, sizeof(ch.txAddress)) < 0) { close(ch.socket); ch.socket = -1; printf("ERROR: bind failed\n"); } return ch; } } printf("Error: unexpected behavior\n"); return ch; } static void close_can_dev(struct can_handler ch) { int socketId = ch.socket; if(socketId >= 0) { close(socketId); } } // Get original get temperature function from cpp hvacplugin code static uint8_t get_temperature(uint8_t value) { uint8_t result = ((0xF0 - 0x10) / 15) * value - 16; if (result < 0x10) result = 0x10; if (result > 0xF0) result = 0xF0; return result; } static void write_can(struct can_handler ch) { // Hardcoded can_id and dlc (data lenght code) struct can_frame txCanFrame; txCanFrame.can_id = 0x30; txCanFrame.can_dlc = 8; if (ch.socket >= 0) { txCanFrame.data[0] = get_temperature(can_hvac_values[0]); txCanFrame.data[1] = get_temperature(can_hvac_values[1]); txCanFrame.data[2] = get_temperature((can_hvac_values[0] + can_hvac_values[1]) & 0x02); txCanFrame.data[3] = can_hvac_values[3]; txCanFrame.data[4] = can_hvac_values[4]; txCanFrame.data[5] = can_hvac_values[5]; txCanFrame.data[6] = can_hvac_values[6]; txCanFrame.data[7] = can_hvac_values[7]; sendto(ch.socket, &txCanFrame, sizeof(struct can_frame), 0, (struct sockaddr*)&ch.txAddress, sizeof(ch.txAddress)); return; } else { printf("Error: socket not initialized\n"); return; } printf("Error sending on CAN bus. Unexpected behavior"); } static __u8 read_temp_left_zone() { return can_hvac_values[0]; } static __u8 read_temp_right_zone() { return can_hvac_values[1]; } static __u8 read_fanspeed() { return can_hvac_values[4]; } static uint8_t make_atoi(const char *str) { return str ? atoi(str) : 0; } /***************************************************************************************/ /***************************************************************************************/ /** **/ /** **/ /** SECTION: BINDING VERBS IMPLEMENTATION **/ /** **/ /** **/ /***************************************************************************************/ /***************************************************************************************/ /* * @brief Simple afb-daemon ping * * @param struct afb_req : an afb request structure * */ static void ping (struct afb_req request) { static int pingcount = 0; json_object *query = afb_req_json(request); afb_req_success_f(request, NULL, "Ping Binder Daemon count=%d query=%s", ++pingcount, json_object_to_json_string(query)); } /* * @brief Get fan speed HVAC system * * @param struct afb_req : an afb request structure * */ static void get_fanspeed(struct afb_req request) { json_object *ret_json; __u8 fanspeed = read_fanspeed(); ret_json = json_object_new_object(); json_object_object_add(ret_json, "FanSpeed", json_object_new_int(fanspeed)); json_object *query = afb_req_json(request); afb_req_success_f(request, ret_json, "Fan Speed is:%d query=%s", fanspeed, json_object_to_json_string(query)); } /* * @brief Read Consign right zone temperature for HVAC system * * @param struct afb_req : an afb request structure * */ static void get_temp_right_zone(struct afb_req request) { json_object *ret_json; __u8 temp = read_temp_right_zone(); ret_json = json_object_new_object(); json_object_object_add(ret_json, "RightTemperature", json_object_new_int(temp)); json_object *query = afb_req_json(request); afb_req_success_f(request, ret_json, "Right zone Temperature is:%d query=%s", temp, json_object_to_json_string(query)); } /* * @brief Read Consign left zone temperature for HVAC system * * @param struct afb_req : an afb request structure * */ static void get_temp_left_zone(struct afb_req request) { json_object *ret_json; __u8 temp = read_temp_left_zone(); ret_json = json_object_new_object(); json_object_object_add(ret_json, "LeftTemperature", json_object_new_int(temp)); json_object *query = afb_req_json(request); afb_req_success_f(request, ret_json, "Left zone Temperature is:%d query=%s", temp, json_object_to_json_string(query)); } /* * @brief Read all values * * @param struct afb_req : an afb request structure * */ static void get_all(struct afb_req request) { json_object *ret_json; ret_json = json_object_new_object(); json_object_object_add(ret_json, "LeftTemperature", json_object_new_int(read_temp_left_zone())); json_object_object_add(ret_json, "RightTemperature", json_object_new_int(read_temp_right_zone())); json_object_object_add(ret_json, "FanSpeed", json_object_new_int(read_fanspeed())); afb_req_success(request, ret_json, NULL); } /* * @brief Set a component value using a json object retrieved from request * * @param struct afb_req : an afb request structure * */ static void set(struct afb_req request) { struct can_handler ch; const char *val; struct json_object *query; struct json_object_iterator iter; struct json_object_iterator iter_end; query = afb_req_json(request); iter = json_object_iter_begin(query); iter_end = json_object_iter_end(query); /* * Loop over the json object that will set every component * which it will find in it. */ int i; while(!json_object_iter_equal(&iter, &iter_end)) { const char *key = json_object_iter_peek_name(&iter); for (i=0;i<8;i++) { if(strcmp(can_hvac_components[i], key) == 0) { val = afb_req_value(request, key); uint8_t comp_val = make_atoi(val); can_hvac_values[i] = comp_val; break; } } json_object_iter_next(&iter); } ch = open_can_dev(); write_can(ch); close_can_dev(ch); afb_req_success(request, query, "HVAC settings updated"); } // TODO: Have to change session management flag to AFB_SESSION_CHECK to use token auth static const struct afb_verb_desc_v1 verbs[]= { {"ping" , AFB_SESSION_NONE, ping , "Ping the binder"}, {"get_temp_left_zone" , AFB_SESSION_NONE, get_temp_left_zone , "Get the left zone temperature"}, {"get_temp_right_zone" , AFB_SESSION_NONE, get_temp_right_zone , "Get the right zone temperature"}, {"get_fanspeed" , AFB_SESSION_NONE, get_fanspeed , "Read fan speed"}, {"get_all" , AFB_SESSION_NONE, get_all , "Read all values"}, {"set" , AFB_SESSION_NONE, set , "Set a HVAC component value"}, {NULL} }; static const struct afb_binding binding_desc = { .type = AFB_BINDING_VERSION_1, .v1 = { .info = "hvac hybrid service", .prefix = "hvac", .verbs = verbs } }; const struct afb_binding *afbBindingV1Register (const struct afb_binding_interface *itf) { interface = itf; return &binding_desc; }