/* * Copyright (C) 2015, 2016 "IoT.bzh" * Author "Romain Forlot" * Author "Jose Bolo" * * 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 #include #define CAN_DEV "can0" static const struct afb_binding_interface *interface; // Initialize CAN hvac array that will be sent trough the socket static char *can_hvac_components[8] = { "LeftTemperature", "RightTemperature", "Temperature", NULL, "FanSpeed", NULL, NULL, NULL }; // Initialize CAN hvac array that will be sent trough the socket static uint8_t can_hvac_values[8] = { 21, // LeftTemperature 21, // RightTemperature 21, // AverageTemperature 240, // Don't know why 240 but it was 0xF0 in the original amb hvacplugin 0, // FanSpeed 1, // Don't know why 1 but it was 0x01 in the original amb hvacplugin 0, // Don't know why 0 but it was 0x00 in the original amb hvacplugin 0 // Don't know why 0 but it was 0x00 in the original amb hvacplugin }; struct can_handler { int socket; struct sockaddr_can txAddress; }; static struct can_handler can_handler = { .socket = -1 }; /*****************************************************************************************/ /*****************************************************************************************/ /** **/ /** **/ /** SECTION: HANDLE CAN DEVICE **/ /** **/ /** **/ /*****************************************************************************************/ /*****************************************************************************************/ static int open_can_dev() { struct ifreq ifr; can_handler.socket = socket(PF_CAN, SOCK_RAW, CAN_RAW); if (can_handler.socket < 0) { ERROR(interface, "socket could not be created"); } else { // Attemts to open a socket to CAN bus strcpy(ifr.ifr_name, CAN_DEV); if(ioctl(can_handler.socket, SIOCGIFINDEX, &ifr) < 0) { ERROR(interface, "ioctl failed"); } else { can_handler.txAddress.can_family = AF_CAN; can_handler.txAddress.can_ifindex = ifr.ifr_ifindex; // And bind it to txAddress if (bind(can_handler.socket, (struct sockaddr *)&can_handler.txAddress, sizeof(can_handler.txAddress)) < 0) { ERROR(interface, "bind failed"); } else { return 0; } } close(can_handler.socket); can_handler.socket = -1; } return -1; } // Get original get temperature function from cpp hvacplugin code static uint8_t get_temperature(uint8_t value) { int result = ((0xF0 - 0x10) / 15) * value - 16; if (result < 0x10) result = 0x10; if (result > 0xF0) result = 0xF0; return (uint8_t)result; } static int write_can() { struct can_frame txCanFrame; int rc = 0; rc = can_handler.socket; if (rc >= 0) { // Hardcoded can_id and dlc (data lenght code) txCanFrame.can_id = 0x30; txCanFrame.can_dlc = 8; 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]) / 2); 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]; rc = sendto(can_handler.socket, &txCanFrame, sizeof(struct can_frame), 0, (struct sockaddr*)&can_handler.txAddress, sizeof(can_handler.txAddress)); if (rc < 0) { ERROR(interface, "Sending can frame failed"); } } else { ERROR(interface, "socket not initialized"); } return rc; } static uint8_t read_temp_left_zone() { return can_hvac_values[0]; } static uint8_t read_temp_right_zone() { return can_hvac_values[1]; } static uint8_t read_fanspeed() { return can_hvac_values[4]; } static int make_uint8(const char *str, uint8_t *value) { long int x; char **end; if (str == NULL) return -1; x = strtol(str, &end, 10); if (end == str || *end) return -1; if (x < 0 || x > 255) return -1; *value = (uint8_t)x; return 0; } /*****************************************************************************************/ /*****************************************************************************************/ /** **/ /** **/ /** SECTION: BINDING VERBS IMPLEMENTATION **/ /** **/ /** **/ /*****************************************************************************************/ /*****************************************************************************************/ /* * @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; uint8_t fanspeed = read_fanspeed(); ret_json = json_object_new_object(); json_object_object_add(ret_json, "FanSpeed", json_object_new_int(fanspeed)); afb_req_success(request, ret_json, NULL); } /* * @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; uint8_t temp = read_temp_right_zone(); ret_json = json_object_new_object(); json_object_object_add(ret_json, "RightTemperature", json_object_new_int(temp)); afb_req_success(request, ret_json, NULL); } /* * @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; uint8_t temp = read_temp_left_zone(); ret_json = json_object_new_object(); json_object_object_add(ret_json, "LeftTemperature", json_object_new_int(temp)); afb_req_success(request, ret_json, NULL); } /* * @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) { int i, rc, nerr, nchg; const char *val, *key; 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. */ nchg = nerr = 0; while(!json_object_iter_equal(&iter, &iter_end)) { key = json_object_iter_peek_name(&iter); i = 0; while (i < 8 && can_hvac_components[i] != NULL && strcmp(can_hvac_components[i], key) != 0) { i++; } if (i < 8) { val = afb_req_value(request, key); rc = make_uint8(val, &can_hvac_values[i]); if (rc < 0) nerr++; else nchg++; } else { // not found! ignore the error silently nerr++; } json_object_iter_next(&iter); } rc = nchg ? write_can() : 0; if (rc < 0) afb_req_fail(request, "error", "CAN error"); else afb_req_success(request, NULL, nerr ? "error detected" : NULL); } // TODO: Have to change session management flag to AFB_SESSION_CHECK to use token auth static const struct afb_verb_desc_v1 verbs[]= { {"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 service", .prefix = "hvac", .verbs = verbs } }; const struct afb_binding *afbBindingV1Register (const struct afb_binding_interface *itf) { interface = itf; return &binding_desc; } int afbBindingV1ServiceInit(struct afb_service service) { return open_can_dev(); }